Claude Code transcript - page 1/2

In a previous session when you were diagnosing a parser bug and parser hangs, I noticed that you created many ad-hoc tests. In these instances you created an occam test program, fed it through the transpiler and checked output for errors. That made me wonder if perhaps you could create additional tests for the transpiler beyond the ones we have now? It appeared that you had the skils and experience to generate many more such tests than are already in the codebase. Does that make sense?

Thinking

The user is asking whether I could create additional tests for the occam2go transpiler. They noticed that during debugging sessions, I was creating ad-hoc test programs and feeding them through the transpiler, and they're suggesting I could formalize this into a more comprehensive test suite.

Let me think about this. The project has: 1. Parser unit tests in parser/parser_test.go 2. Codegen unit tests in codegen/codegen_test.go 3. E2E tests in codegen/e2e_test.go

The user is asking if it makes sense for me to expand the test coverage. Let me look at what's currently tested to understand the gaps before giving a thoughtful answer.

Yes, that makes complete sense. During debugging I was essentially writing mini integration tests on the fly — small occam programs targeting specific constructs, running them through the transpiler, and checking that the output compiled and produced correct results. Those are exactly the kind of tests that should be captured permanently in the test suite.

Let me take a quick look at what's currently covered so I can give you a concrete picture of where the gaps are.

Task
Analyze existing test coverage
{
  "prompt": "Analyze the existing test coverage of the occam2go transpiler. Look at:\n\n1. `codegen/e2e_test.go` - what constructs do the end-to-end tests cover?\n2. `codegen/codegen_test.go` - what constructs do the codegen unit tests cover?\n3. `parser/parser_test.go` - what constructs do the parser tests cover?\n4. `lexer/lexer_test.go` (if it exists) - what does it cover?\n\nFor each file, list the test function names and briefly note what language feature each one tests. Then compare against the \"What's Implemented\" section in CLAUDE.md to identify gaps - features that are implemented but have little or no test coverage.\n\nReturn a structured summary with:\n- Current coverage by feature area\n- Features with NO test coverage\n- Features with only partial coverage (e.g., only parser tests but no e2e test)\n",
  "subagent_type": "Explore",
  "model": "sonnet"
}
[
  {
    "type": "text",
    "text": "Now I have a complete picture. Let me compile the full analysis.\n\n---\n\n## Test Coverage Analysis for occam2go\n\n### Test File Inventory\n\n- **`lexer/lexer_test.go`**: 9 test functions\n- **`parser/parser_test.go`**: 80 test functions\n- **`codegen/codegen_test.go`**: 49 unit test functions (output-checking, no execution)\n- **`codegen/e2e_basic_test.go`**: 10 e2e tests\n- **`codegen/e2e_control_test.go`**: 15 e2e tests\n- **`codegen/e2e_concurrency_test.go`**: 14 e2e tests\n- **`codegen/e2e_array_test.go`**: 14 e2e tests\n- **`codegen/e2e_proc_func_test.go`**: 19 e2e tests\n- **`codegen/e2e_protocol_test.go`**: 5 e2e tests\n- **`codegen/e2e_record_test.go`**: 4 e2e tests\n- **`codegen/e2e_replicator_test.go`**: 9 e2e tests\n- **`codegen/e2e_types_test.go`**: 26 e2e tests\n- **`codegen/e2e_phase2_test.go`**: 4 e2e tests\n- **`codegen/e2e_preproc_test.go`**: 3 e2e tests\n- **`codegen/e2e_course_test.go`**: 1 e2e test (real-world integration)\n- **`preproc/preproc_test.go`**: 22 unit tests\n- **`modgen/modgen_test.go`**: 5 unit tests\n\n---\n\n### Coverage by Feature Area\n\n#### Lexer (`lexer/lexer_test.go`)\n| Test | Feature |\n|------|---------|\n| `TestBasicTokens` | Basic token types (INT, IDENT, COLON, ASSIGN, INT literal) |\n| `TestIndentation` | INDENT/DEDENT generation from 2-space indentation |\n| `TestOperators` | Arithmetic and comparison operators |\n| `TestKeywords` | Core keyword recognition (SEQ, PAR, ALT, IF, WHILE, PROC, types) |\n| `TestComments` | `--` comment stripping |\n| `TestBitwiseOperators` | `/\\` `\\/` `><` `~` `<<` `>>` |\n| `TestBitwiseVsArithmetic` | `/` vs `\\/`, `\\` (modulo) disambiguation |\n| `TestHexLiterals` | `#FF`, `#1a2b`, `#0` hex literal tokenization |\n| `TestNestedIndentation` | Multiple indentation levels |\n\n**Lexer gaps**: No tests for: `parenDepth` suppression (paren/bracket suppression of INDENT/DEDENT), continuation-operator line continuation (`lastTokenType` tracking), string literals, byte literals, `'*n'` escape sequences, TIMER keyword, `?` and `!` channel operators, ALL occam keywords beyond the 11 tested (CASE, STOP, SKIP, FUNC, VAL, CHAN, REAL32, REAL64, etc.), AFTER keyword, PROTOCOL, RECORD, INITIAL, MOSTNEG, MOSTPOS, PLUS/MINUS/TIMES (checked arithmetic keywords), RETYPES.\n\n---\n\n#### Parser (`parser/parser_test.go`)\n| Test | Feature |\n|------|---------|\n| `TestVarDecl` | `INT x:` single variable |\n| `TestMultipleVarDecl` | `INT x, y:` multi-variable |\n| `TestAssignment` | `x := expr` |\n| `TestBinaryExpression` | Binary infix expressions |\n| `TestSeqBlock` | `SEQ` block |\n| `TestParBlock` | `PAR` block |\n| `TestChanDecl` | `CHAN OF INT c:` |\n| `TestSend` | `c ! expr` |\n| `TestReceive` | `c ? x` |\n| `TestAltBlock` | `ALT` with channel guards |\n| `TestAltBlockWithGuard` | `ALT` with boolean guards (`TRUE & c ? x`) |\n| `TestWhileLoop` | `WHILE` |\n| `TestIfStatement` | `IF` multi-branch |\n| `TestReplicatedSeq` | `SEQ i = 0 FOR n` |\n| `TestReplicatedSeqWithStep` | `SEQ i = 0 FOR n STEP s` |\n| `TestReplicatedPar` | `PAR i = 0 FOR n` |\n| `TestReplicatedIf` | `IF i = 0 FOR n` |\n| `TestArrayDecl` | `[5]INT arr:` |\n| `TestArrayDeclMultipleNames` | `[5]INT a, b:` |\n| `TestIndexedAssignment` | `arr[i] := expr` |\n| `TestIndexExpression` | `arr[expr]` in expressions |\n| `TestFuncDeclIS` | `INT FUNCTION f(VAL INT x) IS expr` |\n| `TestFuncDeclValof` | `INT FUNCTION f(...) VALOF ... RESULT expr` |\n| `TestMultiResultFuncDecl` | `INT, INT FUNCTION f(...)` |\n| `TestMultiAssignment` | `a, b := f()` |\n| `TestMultiAssignmentIndexed` | `x[0], x[1] := x[1], x[0]` |\n| `TestMultiAssignmentMixed` | `a, x[0] := ...` |\n| `TestCaseStatement` | `CASE x` with branches and ELSE |\n| `TestTimerDecl` | `TIMER tim:` |\n| `TestTimerRead` | `tim ? t` |\n| `TestAfterExpression` | `t2 AFTER t1` |\n| `TestChanParam` | `PROC f(CHAN OF INT c)` |\n| `TestChanParamMixed` | `PROC f(CHAN OF INT c, VAL INT x)` |\n| `TestTypeConversion` | `INT y`, `BYTE n` type cast expressions |\n| `TestTypeConversionInExpression` | Type cast in binary expr |\n| `TestStringLiteral` | `\"hello*n\"` with occam escapes |\n| `TestStringEscapeConversion` | `*n` → `\\n`, `*c` → `\\r` etc. |\n| `TestByteLiteral` | `'A'`, `'*n'` |\n| `TestStringLiteralInProcCall` | String passed as arg |\n| `TestSimpleProtocolDecl` | `PROTOCOL X IS INT` |\n| `TestSequentialProtocolDecl` | `PROTOCOL X IS INT ; INT` |\n| `TestVariantProtocolDecl` | `PROTOCOL X CASE tag; INT` |\n| `TestChanDeclWithProtocol` | `CHAN OF SIGNAL c:` |\n| `TestSequentialSend` | `c ! 10 ; 20` |\n| `TestRecordDecl` | `RECORD POINT INT x: INT y:` |\n| `TestRecordDeclMultipleFieldNames` | Record with multiple field names |\n| `TestRecordVarDecl` | `POINT p:` |\n| `TestRecordFieldAssignment` | `p[x] := 5` |\n| `TestRecordFieldAccess` | `p[x]` in expression |\n| `TestChanArrayDecl` | `[5]CHAN OF INT cs:` |\n| `TestIndexedSend` | `cs[i] ! v` |\n| `TestIndexedReceive` | `cs[i] ? x` |\n| `TestChanArrayParam` | `PROC f([]CHAN OF INT cs)` |\n| `TestChanDirParam` | `PROC f(CHAN OF INT c?)` / `c!` |\n| `TestChanArrayDirParam` | `PROC f([]CHAN OF INT cs?)` |\n| `TestSequentialReceive` | `c ? x ; y` |\n| `TestSizeExpression` | `SIZE arr` |\n| `TestSizeExpressionInBinaryExpr` | `SIZE arr + 1` |\n| `TestMostNegExpression` | `MOSTNEG INT` |\n| `TestMostPosExpression` | `MOSTPOS INT` |\n| `TestMostNegInBinaryExpr` | `MOSTNEG INT + 1` |\n| `TestValAbbreviation` | `VAL INT x IS 42:` |\n| `TestNonValAbbreviation` | `INT y IS z:` |\n| `TestValBoolAbbreviation` | `VAL BOOL flag IS TRUE:` |\n| `TestAbbreviationWithExpression` | `VAL INT b IS (a + 5):` |\n| `TestInitialDecl` | `INITIAL INT x IS 42:` |\n| `TestInitialDeclWithExpression` | `INITIAL INT x IS expr:` |\n| `TestOpenArrayParam` | `PROC f(VAL []INT arr)`, `[]BYTE` |\n| `TestChanDeclShorthand` | `CHAN INT c:` (without `OF`) |\n| `TestChanArrayDeclShorthand` | `[5]CHAN INT cs:` |\n| `TestChanParamShorthand` | `PROC f(CHAN INT c)` |\n| `TestHexIntegerLiteral` | `#FF` in expression |\n| `TestHexIntegerLiteralLarge` | `#80000000` |\n| `TestNestedProcDecl` | `PROC outer() PROC inner() ...` |\n| `TestNestedFuncDecl` | Nested function in proc |\n| `TestProcLocalVarDecls` | Local var decls in proc body |\n| `TestCheckedArithmeticOperators` | `PLUS`, `MINUS`, `TIMES` |\n| `TestCheckedArithmeticPrecedence` | Checked ops in expressions |\n| `TestCheckedAndSymbolMixed` | `PLUS` mixed with `+` |\n| `TestMultiStatementIfBody` | IF body with scoped decl |\n| `TestChannelDirAtCallSite` | `f(out!, in?)` call-site direction |\n| `TestUntypedValAbbreviation` | `VAL x IS 42:` (untyped) |\n| `TestArrayLiteral` | `[1, 2, 3]` |\n| `TestRetypesDecl` | `VAL INT X RETYPES Y:` |\n| `TestRetypesDeclArray` | `VAL [2]INT X RETYPES Y:` |\n| `TestMultiLineBooleanExpression` | Multi-line expr with AND continuation |\n\n**Parser gaps**: No tests for `SKIP` as a standalone statement, `STOP` as a statement, `VAL []BYTE s IS \"hi\":` (open array abbreviation), `PROC f(CHAN INT a?, b?)` (shared-type params), `PROC f(RESULT INT x)` (RESULT qualifier), fixed-size array params `[n]TYPE`, transputer intrinsics as function calls (LONGPROD etc.), variant receive `c ? CASE`, timer ALT arm (`tim ? AFTER ...`).\n\n---\n\n#### Codegen Unit Tests (`codegen/codegen_test.go`)\n| Test | Feature |\n|------|---------|\n| `TestSimpleVarDecl` | `var x int` output |\n| `TestMultipleVarDecl` | `var x, y, z int` |\n| `TestReal32VarDecl` | `var x float32` |\n| `TestReal64VarDecl` | `var x float64` |\n| `TestAssignment` | `x = 42` |\n| `TestBinaryExpression` | `x = (a + b)` |\n| `TestComparisonOperators` | `==` and `!=` mapping |\n| `TestSeqBlock` | SEQ → sequential Go |\n| `TestParBlock` | PAR → WaitGroup + goroutines |\n| `TestProcDecl` | `func foo(x int)` |\n| `TestProcDeclWithRefParam` | Non-VAL → `*int` pointer |\n| `TestIfStatement` | `if / else if` chain |\n| `TestReplicatedIf` | Replicated IF → for loop with break |\n| `TestArrayDecl` | `make([]int, 5)` |\n| `TestIndexedAssignment` | `arr[2] = 10` |\n| `TestBitwiseOperators` | `&`, `|`, `^`, `<<`, `>>`, `^` (NOT) |\n| `TestStringLiteral` | String passthrough |\n| `TestStringEscapeCodegen` | Occam `*c*n` → `\\r\\n` |\n| `TestByteLiteral` | `'A'` → `byte(65)` |\n| `TestByteLiteralEscape` | `'*n'` → `byte(10)` |\n| `TestStop` | `STOP` → `fmt.Fprintln` + `select {}` |\n| `TestTypeConversion` | INT/BYTE/REAL/BOOL/REAL32/REAL64 casts |\n| `TestMostNegMostPos` | All 8 MOSTNEG/MOSTPOS combinations |\n| `TestMostNegImportsMath` | `math` import for INT bounds |\n| `TestMostNegByteNoMathImport` | No math import for BYTE bounds |\n| `TestStringLiteralInProcCall` | `print.string` → `fmt.Println` |\n| `TestCheckedArithmeticCodegen` | PLUS/MINUS/TIMES → `+/-/*` |\n| `TestSimpleProtocolType` | `type _proto_X = int` |\n| `TestSequentialProtocolType` | `type _proto_X struct {...}` |\n| `TestVariantProtocolType` | Interface + concrete structs |\n| `TestRecordType` | `type POINT struct {...}` |\n| `TestRecordFieldAssignmentCodegen` | `p.x = 5` |\n| `TestChanArrayDeclGen` | `make([]chan int, 5)` + init loop |\n| `TestIndexedSendGen` | `cs[0] <- 42` |\n| `TestIndexedReceiveGen` | `x = <-cs[0]` |\n| `TestChanArrayParamGen` | `func worker(cs []chan int)` |\n| `TestChanDirParamGen` | `<-chan int` / `chan<- int` |\n| `TestChanArrayDirParamGen` | `[]<-chan int` / `[]chan<- int` |\n| `TestRecordFieldAccessCodegen` | `v = p.x` |\n| `TestSizeOperator` | `len(arr)` |\n| `TestOpenArrayParamGen` | `[]int`, `[]byte` slice params |\n| `TestAbbreviation` | VAL/non-VAL/INITIAL abbreviations |\n| `TestMultiAssignmentSimple` | `a, b = 1, 2` |\n| `TestMultiAssignmentIndexed` | `x[0], x[1] = x[1], x[0]` |\n| `TestMultiAssignmentMixed` | `a, x[0] = 1, 2` |\n| `TestArrayLiteralCodegen` | `[]int{10, 20, 30}` |\n| `TestUntypedValCodegen` | `var x = 42` |\n| `TestCAUSEERROR` | `panic(\"CAUSEERROR\")` |\n| `TestGoIdentByteReserved` | Go reserved word escaping (`_byte`) |\n\n**Codegen unit test gaps**: No unit tests checking: RETYPES code output (`int(int32(math.Float32bits(...)))` pattern), transputer intrinsic helper function emission, LONGPROD/LONGDIV/etc. call generation, ALT with guard condition emission, timer ALT arm (`time.Now()` + select case), sequential protocol send/receive code, variant protocol send/receive code, `PROC f(RESULT INT x)` → `func f(x *int)`, fixed-size array param `[n]TYPE` → `*[n]type`, shared-type channel params, `VAL []BYTE s IS \"hi\":` abbreviation code.\n\n---\n\n#### E2E Tests (all e2e_*.go files)\n| Test | Feature |\n|------|---------|\n| `TestE2E_PrintInt` | Basic SEQ + variable + print.int |\n| `TestE2E_Addition/Multiplication/Expression/MultiplePrints` | Arithmetic |\n| `TestE2E_Boolean` | BOOL type + TRUE literal |\n| `TestE2E_Comparison` | `>` operator |\n| `TestE2E_ComplexExpression` | Precedence, division |\n| `TestE2E_StopNotTaken` | STOP in untaken IF branch |\n| `TestE2E_InitialDecl` | `INITIAL INT x IS 10:` |\n| `TestE2E_IfBasic/IfSecondBranch/IfThreeBranches/IfWithSeqBody` | IF branches |\n| `TestE2E_WhileBasic/WhileSum/WhileNested` | WHILE loops |\n| `TestE2E_CaseBasic/CaseElse/CaseExpression` | CASE statement |\n| `TestE2E_MultiStatementIfBody/CaseBody/WhileBody` | Multi-stmt ALT/IF/CASE bodies |\n| `TestE2E_NestedReplicatedIf*` | Nested replicated IF patterns |\n| `TestE2E_NestedNonReplicatedIf` | Inlined nested IF |\n| `TestE2E_ChannelDirAtCallSite` | `f(a?, b!)` call-site direction |\n| `TestE2E_PAR` | Basic PAR (goroutines + WaitGroup) |\n| `TestE2E_Channel/ChannelExpression/ChannelPingPong` | Channel send/receive |\n| `TestE2E_AltBasic/AltSecondChannel/AltWithBody` | ALT channel selection |\n| `TestE2E_TimerRead` | `TIMER tim: tim ? t` |\n| `TestE2E_TimerAltTimeout` | ALT with timer `tim ? AFTER` arm |\n| `TestE2E_ChanParam/ChanParamTwoWay/ChanDirParam` | Channel proc params |\n| `TestE2EChanShorthand` | `CHAN INT c:` shorthand |\n| `TestE2E_ArrayBasic/ArrayWithLoop/ArraySum/ArrayExpressionIndex` | Arrays |\n| `TestE2E_ChanArrayBasic/ChanArrayWithProc/ChanArrayAlt` | Channel arrays |\n| `TestE2E_SizeArray/SizeString` | SIZE operator |\n| `TestE2E_SliceAsArg/SliceAssignment/SliceSize/SliceFromZero` | Array slices |\n| `TestE2E_OpenArrayParam` | `VAL []INT arr` proc param |\n| `TestE2E_MultiAssignmentSwap/Mixed/Values` | Multi-assignment |\n| `TestE2E_Procedure` | PROC with ref param |\n| `TestE2E_FunctionIS/FunctionValof/FunctionInExpr` | FUNCTION (IS and VALOF) |\n| `TestE2E_ValAbbreviation/AbbreviationWithExpression` | VAL abbreviations |\n| `TestE2E_ProcColonTerminator/FunctionISColonTerminator/...` | KRoC `:` terminator |\n| `TestE2E_MultiResultFunction/ThreeValues` | Multi-result functions |\n| `TestE2E_NonValAbbreviation` | `INT y IS z:` |\n| `TestE2E_NestedProcClosure/WithParams/NestedFunctionIS/Valof/MultipleNestedProcs` | Nested procs/funcs |\n| `TestE2E_ProcLocalVarDecls` | Local vars in proc |\n| `TestE2E_SimpleProtocol/SequentialProtocol/VariantProtocol/NoPayload/WithProc` | Protocols |\n| `TestE2E_RecordBasic/WithProc/WithValProc/InExpression` | Records |\n| `TestE2E_ReplicatedSeq/WithExpression/WithStartOffset/Sum` | Replicated SEQ |\n| `TestE2E_ReplicatedPar` | Replicated PAR |\n| `TestE2E_ReplicatedIf` | Replicated IF |\n| `TestE2E_ReplicatedSeqStep/NegativeStep/ReplicatedParStep` | STEP replicator |\n| `TestE2E_TypeConversion*` | INT/BYTE/REAL32/REAL64 conversions |\n| `TestE2E_Real32VarDecl/Real64VarDecl/Real32ToReal64Conversion/Real32Array` | Float types |\n| `TestE2E_HexLiteral/HexLiteralInExpression` | Hex literals |\n| `TestE2E_BitwiseAnd/Or/Xor/Not/LeftShift/RightShift` | Bitwise operators |\n| `TestE2E_AfterExpression` | `AFTER` in IF |\n| `TestE2E_ByteLiteral/ByteLiteralEscape` | Byte literals |\n| `TestE2E_MostNeg/MostPos variants` | MOSTNEG/MOSTPOS for INT and BYTE |\n| `TestE2E_CheckedArithmetic PLUS/MINUS/TIMES/Wrapping` | Checked arithmetic |\n| `TestE2E_UntypedValAbbreviation` | `VAL x IS 42:` |\n| `TestE2E_ArrayLiteralIndexing` | `[10, 20, 30]` |\n| `TestE2E_MultiLineBooleanIF` | Multi-line AND expression |\n| `TestE2E_CAUSEERROR` | `CAUSEERROR()` → panic |\n| `TestE2E_IncludeConstants/IfFalseExcludes/IncludeGuardPreventsDouble` | Preprocessor |\n| `TestE2E_HelloWorldCourseModule` | Real-world course module integration |\n\n---\n\n### Features with NO Test Coverage\n\nThese are implemented (per CLAUDE.md \"What's Implemented\") but have no tests at any level:\n\n1. **Transputer intrinsics as function calls (LONGPROD, LONGDIV, LONGSUM, LONGDIFF, NORMALISE, SHIFTRIGHT, SHIFTLEFT)** - Only found in `parser_test.go` as `TestRetypesDecl`/`TestRetypesDeclArray` for RETYPES, but the intrinsic calls themselves (e.g., `a, b := LONGPROD(x, y, c)`) have no parser, codegen unit, or e2e tests at all.\n\n2. **RETYPES codegen output** - Parser tests exist (`TestRetypesDecl`, `TestRetypesDeclArray`) but there is no codegen unit test checking the generated `int(int32(math.Float32bits(float32(X))))` pattern, and no e2e test running RETYPES code end-to-end.\n\n3. **`VAL []BYTE s IS \"hi\":` (open array byte abbreviation)** - No test at any level.\n\n4. **`PROC f(RESULT INT x)` (RESULT qualifier parameter)** - No test at any level (distinct from regular non-VAL reference params).\n\n5. **Fixed-size array params `[n]TYPE` → `*[n]type`** - No test; `TestOpenArrayParam` only tests `[]INT` open-array params, not fixed-size array params passed by pointer.\n\n6. **Shared-type channel params `PROC f(CHAN INT a?, b?)` (type applies to multiple names)** - No test at any level.\n\n7. **MOSTNEG/MOSTPOS for REAL32 and REAL64** - Only INT and BYTE have e2e tests; REAL32/REAL64 bounds are covered only in codegen unit test `TestMostNegMostPos` (output check) with no e2e execution test.\n\n8. **`\\` (modulo operator) in e2e** - `TestBitwiseVsArithmetic` in the lexer tokenizes `\\` as MODULO, and `codegen_test.go` doesn't test it either; no e2e test uses `\\`.\n\n9. **ALT with boolean guard in e2e** - `TestAltBlockWithGuard` in parser confirms parsing, but there is no e2e test executing a guarded ALT (`TRUE & c ? x` style).\n\n10. **`print.string` in e2e** - Only tested at parser/codegen unit level; no e2e test actually runs `print.string`.\n\n11. **`print.newline` / `print.nl`** - Not tested at any level.\n\n12. **`parenDepth` / bracket suppression of INDENT/DEDENT in lexer** - The lexer feature exists but has no lexer unit test specifically for it (the e2e multi-line expression test exercises it indirectly via the parser).\n\n---\n\n### Features with Only Partial Coverage\n\nThese have parser tests and/or codegen unit tests but no e2e execution test:\n\n| Feature | Parser Test | Codegen Unit | E2E |\n|---------|-------------|--------------|-----|\n| RETYPES (`VAL INT X RETYPES Y`) | Yes (2 tests) | No | No |\n| Transputer intrinsics (LONGPROD etc.) | No | No | No |\n| `VAL []BYTE s IS \"hi\":` | No | No | No |\n| RESULT qualifier param | No | No | No |\n| Fixed-size array param `[n]TYPE` | No | No | No |\n| Shared-type channel params | No | No | No |\n| MOSTNEG/MOSTPOS REAL32/REAL64 | No | Yes | No |\n| Modulo operator `\\` | Lexer only | No | No |\n| ALT with boolean guard | Parser only | No | No |\n| `print.string` / `print.newline` | Parser + codegen unit | Codegen unit | No |\n| `STOP` actually taken (program hangs) | No | Codegen unit | No (only not-taken) |\n| `goIdent` Go reserved word escaping | No | Yes (1: `byte`) | No |\n| Continuation-operator line continuation | Parser (multi-line bool) | Indirect | Indirect |\n| Lexer paren/bracket depth suppression | Indirect | Indirect | Indirect |\n| `VAL x IS [1,2,3]` untyped array literal | Parser yes | Codegen unit yes | E2E yes (indexing only) |\n| `[arr FOR m]` shorthand slice (FROM 0 implied) | Indirect | Indirect | No direct test |\n| `SKIP` as a standalone statement | Implicit in many tests | Implicit | No direct test |\n| KRoC-style colon terminator on PROC/FUNC | No parser test | No codegen unit | E2E yes (4 tests) |\n| Multi-line proc parameter lists | No lexer test | Indirect | Indirect |\n| String→`[]byte` wrapping for `[]BYTE` params | No | No | No direct test |\n\n### Summary of the Biggest Gaps\n\nThe most significant untested areas are:\n\n1. **Transputer intrinsics** (LONGPROD, LONGDIV, LONGSUM, LONGDIFF, NORMALISE, SHIFTRIGHT, SHIFTLEFT) - zero tests at any level despite being implemented.\n2. **RETYPES codegen and execution** - parser tests exist but no verification that generated code compiles and produces the right bit-pattern reinterpretation.\n3. **RESULT qualifier on proc params** - completely untested.\n4. **Fixed-size array params** - completely untested.\n5. **Shared-type channel params** - completely untested.\n6. **`VAL []BYTE s IS \"hi\":` abbreviation** - completely untested.\n7. **ALT with boolean guards (e2e)** - only parser-level coverage.\n8. **MOSTNEG/MOSTPOS for floating-point types (e2e)** - only codegen unit output check.\n9. **The modulo operator `\\`** - only tokenizer-level recognition.\n10. **`print.string` / `print.newline` (e2e)** - no end-to-end execution test."
  },
  {
    "type": "text",
    "text": "agentId: ac0b1f7 (for resuming to continue this agent's work if needed)\n<usage>total_tokens: 89560\ntool_uses: 49\nduration_ms: 216718</usage>"
  }
]

That's a thorough analysis. Here's the summary:

Current state: ~124 e2e tests, ~49 codegen unit tests, ~80 parser tests, ~9 lexer tests, ~22 preprocessor tests.

Biggest gaps with zero or near-zero test coverage:

  1. Transputer intrinsics (LONGPROD, LONGDIV, LONGSUM, etc.) — zero tests at any level
  2. RETYPES — parser tests only, no codegen/e2e verification that bit-reinterpretation actually works
  3. RESULT qualifier on proc params — completely untested
  4. Fixed-size array params [n]TYPE → pointer — completely untested
  5. Shared-type channel params PROC f(CHAN INT a?, b?) — completely untested
  6. VAL []BYTE s IS "hi": abbreviation — completely untested
  7. ALT with boolean guards — parser test only, no e2e
  8. MOSTNEG/MOSTPOS for REAL32/REAL64 — codegen unit only, no e2e
  9. Modulo operator \ — lexer tokenization only
  10. print.string / print.newline — no e2e execution

Partially covered features include STOP (only the "not taken" path), lexer paren-depth suppression (only exercised indirectly), and string-to-[]byte wrapping.

So yes — I could write a substantial batch of new tests covering all of these gaps. The e2e tests are the highest-value ones since they verify the full pipeline (parse → codegen → compile → run → check output). Want me to go ahead and write them? I could tackle them as a batch, or focus on specific areas you care about most.

Yes please. Go ahead and work on this for a while. I have coffee to drink.

Thinking

The user wants me to create a comprehensive set of new tests covering the gaps identified. Let me organize this into tasks and work through them systematically. I'll focus primarily on e2e tests since those provide the most value, but I'll also add some unit tests where appropriate.

Let me create tasks and then start working through them.

Enjoy your coffee! I'll work through all those gaps systematically.

TaskCreate
Add e2e tests for all 7 transputer intrinsics: LONGPROD, LONGDIV, LONGSUM, LONGDIFF, NORMALISE, SHIFTRIGHT, SHIFTLEFT. Each test should construct an occam program that calls the intrinsic and prints the result.
{
  "subject": "Add e2e tests for transputer intrinsics (LONGPROD, LONGDIV, etc.)",
  "activeForm": "Adding transputer intrinsic tests"
}
Task #1 created successfully: Add e2e tests for transputer intrinsics (LONGPROD, LONGDIV, etc.)
TaskCreate
Add e2e tests for VAL INT X RETYPES Y (float32→int bit reinterpretation) and VAL [2]INT X RETYPES Y (float64→int pair). Verify the bit patterns are correct.
{
  "subject": "Add e2e tests for RETYPES",
  "activeForm": "Adding RETYPES e2e tests"
}
Task #2 created successfully: Add e2e tests for RETYPES
TaskCreate
Add e2e tests for: PROC f(RESULT INT x) — RESULT qualifier on params; PROC f([2]INT arr) — fixed-size array params; PROC f(CHAN INT a?, b?) — shared-type channel params.
{
  "subject": "Add e2e tests for RESULT qualifier, fixed-size array params, shared-type params",
  "activeForm": "Adding proc parameter variant tests"
}
Task #3 created successfully: Add e2e tests for RESULT qualifier, fixed-size array params, shared-type params
TaskCreate
Add e2e tests for: VAL []BYTE s IS "hello": abbreviation; print.string procedure; print.newline / print.nl procedures.
{
  "subject": "Add e2e tests for VAL []BYTE abbreviation, print.string, print.newline",
  "activeForm": "Adding string/print e2e tests"
}
Task #4 created successfully: Add e2e tests for VAL []BYTE abbreviation, print.string, print.newline
TaskCreate
Add e2e tests for: ALT with boolean guard (TRUE & c ? x); modulo operator (\); MOSTNEG/MOSTPOS for REAL32 and REAL64.
{
  "subject": "Add e2e tests for ALT with boolean guard, modulo operator, MOSTNEG/MOSTPOS REAL",
  "activeForm": "Adding ALT guard, modulo, and REAL bounds tests"
}
Task #5 created successfully: Add e2e tests for ALT with boolean guard, modulo operator, MOSTNEG/MOSTPOS REAL
TaskCreate
Add lexer unit tests for: parenDepth suppression of INDENT/DEDENT/NEWLINE inside parens and brackets; continuation-operator line continuation; keywords not currently tested (CASE, STOP, SKIP, FUNC, VAL, CHAN, REAL32, REAL64, PROTOCOL, RECORD, INITIAL, MOSTNEG, MOSTPOS, PLUS, MINUS, TIMES, RETYPES, AFTER, RESULT).
{
  "subject": "Add lexer tests for paren depth, continuation, and missing keywords",
  "activeForm": "Adding lexer unit tests"
}
Task #6 created successfully: Add lexer tests for paren depth, continuation, and missing keywords
TaskCreate
Add e2e tests for: SKIP as standalone statement; string→[]byte wrapping when passing string literal to []BYTE param; [arr FOR m] shorthand slice (FROM 0 implied). STOP is tricky since it hangs, but we could test it's reached via stderr output.
{
  "subject": "Add misc e2e tests: SKIP, STOP, string→[]byte, shorthand slice",
  "activeForm": "Adding miscellaneous e2e tests"
}
Task #7 created successfully: Add misc e2e tests: SKIP, STOP, string→[]byte, shorthand slice
TaskCreate
Run go test ./... to verify all new tests pass alongside existing tests. Fix any failures.
{
  "subject": "Run full test suite to verify all new tests pass",
  "activeForm": "Running full test suite"
}
Task #8 created successfully: Run full test suite to verify all new tests pass

Let me start by reading the existing e2e test files to understand the patterns used.

TaskUpdate
{
  "taskId": "1",
  "status": "in_progress"
}
Updated task #1 status
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/codegen/e2e_basic_test.go"
}
     1→package codegen
     2→
     3→import (
     4→	"strings"
     5→	"testing"
     6→)
     7→
     8→func TestE2E_PrintInt(t *testing.T) {
     9→	occam := `SEQ
    10→  INT x:
    11→  x := 42
    12→  print.int(x)
    13→`
    14→	output := transpileCompileRun(t, occam)
    15→	expected := "42\n"
    16→	if output != expected {
    17→		t.Errorf("expected %q, got %q", expected, output)
    18→	}
    19→}
    20→
    21→func TestE2E_Addition(t *testing.T) {
    22→	occam := `SEQ
    23→  INT x, y:
    24→  x := 10
    25→  y := 20
    26→  print.int(x + y)
    27→`
    28→	output := transpileCompileRun(t, occam)
    29→	expected := "30\n"
    30→	if output != expected {
    31→		t.Errorf("expected %q, got %q", expected, output)
    32→	}
    33→}
    34→
    35→func TestE2E_Multiplication(t *testing.T) {
    36→	occam := `SEQ
    37→  INT a, b, c:
    38→  a := 3
    39→  b := 4
    40→  c := a * b
    41→  print.int(c)
    42→`
    43→	output := transpileCompileRun(t, occam)
    44→	expected := "12\n"
    45→	if output != expected {
    46→		t.Errorf("expected %q, got %q", expected, output)
    47→	}
    48→}
    49→
    50→func TestE2E_Expression(t *testing.T) {
    51→	occam := `SEQ
    52→  INT result:
    53→  result := (2 + 3) * 4
    54→  print.int(result)
    55→`
    56→	output := transpileCompileRun(t, occam)
    57→	expected := "20\n"
    58→	if output != expected {
    59→		t.Errorf("expected %q, got %q", expected, output)
    60→	}
    61→}
    62→
    63→func TestE2E_MultiplePrints(t *testing.T) {
    64→	occam := `SEQ
    65→  print.int(1)
    66→  print.int(2)
    67→  print.int(3)
    68→`
    69→	output := transpileCompileRun(t, occam)
    70→	expected := "1\n2\n3\n"
    71→	if output != expected {
    72→		t.Errorf("expected %q, got %q", expected, output)
    73→	}
    74→}
    75→
    76→func TestE2E_Boolean(t *testing.T) {
    77→	occam := `SEQ
    78→  BOOL flag:
    79→  flag := TRUE
    80→  print.bool(flag)
    81→`
    82→	output := transpileCompileRun(t, occam)
    83→	expected := "true\n"
    84→	if output != expected {
    85→		t.Errorf("expected %q, got %q", expected, output)
    86→	}
    87→}
    88→
    89→func TestE2E_Comparison(t *testing.T) {
    90→	occam := `SEQ
    91→  INT a, b:
    92→  a := 5
    93→  b := 3
    94→  print.bool(a > b)
    95→`
    96→	output := transpileCompileRun(t, occam)
    97→	expected := "true\n"
    98→	if output != expected {
    99→		t.Errorf("expected %q, got %q", expected, output)
   100→	}
   101→}
   102→
   103→func TestE2E_ComplexExpression(t *testing.T) {
   104→	// Test: (10 + 5) * 2 - 6 / 3 = 15 * 2 - 2 = 30 - 2 = 28
   105→	occam := `SEQ
   106→  INT result:
   107→  result := ((10 + 5) * 2) - (6 / 3)
   108→  print.int(result)
   109→`
   110→	output := transpileCompileRun(t, occam)
   111→	// Note: Need to verify Go's integer division matches expectation
   112→	output = strings.TrimSpace(output)
   113→	if output != "28" {
   114→		t.Errorf("expected 28, got %q", output)
   115→	}
   116→}
   117→
   118→func TestE2E_StopNotTaken(t *testing.T) {
   119→	// Test STOP in an IF branch that is NOT taken — program completes normally
   120→	occam := `SEQ
   121→  INT x:
   122→  x := 1
   123→  IF
   124→    x = 0
   125→      STOP
   126→    TRUE
   127→      print.int(42)
   128→`
   129→	output := transpileCompileRun(t, occam)
   130→	expected := "42\n"
   131→	if output != expected {
   132→		t.Errorf("expected %q, got %q", expected, output)
   133→	}
   134→}
   135→
   136→func TestE2E_InitialDecl(t *testing.T) {
   137→	occam := `SEQ
   138→  INITIAL INT x IS 10:
   139→  print.int(x)
   140→  x := x + 5
   141→  print.int(x)
   142→`
   143→	output := transpileCompileRun(t, occam)
   144→	expected := "10\n15\n"
   145→	if output != expected {
   146→		t.Errorf("expected %q, got %q", expected, output)
   147→	}
   148→}
   149→
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/codegen/e2e_test.go"
}
File does not exist. Note: your current working directory is /home/david/projects/code-associates/occam2go.
Glob
{
  "pattern": "codegen/e2e_*test*.go"
}
/home/david/projects/code-associates/occam2go/codegen/e2e_concurrency_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_preproc_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_helpers_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_protocol_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_record_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_proc_func_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_replicator_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_array_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_basic_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_types_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_control_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_phase2_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_course_test.go
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/codegen/e2e_helpers_test.go"
}
     1→package codegen
     2→
     3→import (
     4→	"os"
     5→	"os/exec"
     6→	"path/filepath"
     7→	"testing"
     8→
     9→	"github.com/codeassociates/occam2go/lexer"
    10→	"github.com/codeassociates/occam2go/parser"
    11→	"github.com/codeassociates/occam2go/preproc"
    12→)
    13→
    14→// transpileCompileRun takes Occam source, transpiles to Go, compiles, runs,
    15→// and returns the stdout output
    16→func transpileCompileRun(t *testing.T, occamSource string) string {
    17→	t.Helper()
    18→
    19→	// Transpile
    20→	l := lexer.New(occamSource)
    21→	p := parser.New(l)
    22→	program := p.ParseProgram()
    23→
    24→	if len(p.Errors()) > 0 {
    25→		for _, err := range p.Errors() {
    26→			t.Errorf("parser error: %s", err)
    27→		}
    28→		t.FailNow()
    29→	}
    30→
    31→	gen := New()
    32→	goCode := gen.Generate(program)
    33→
    34→	// Create temp directory for this test
    35→	tmpDir, err := os.MkdirTemp("", "occam2go-test-*")
    36→	if err != nil {
    37→		t.Fatalf("failed to create temp dir: %v", err)
    38→	}
    39→	defer os.RemoveAll(tmpDir)
    40→
    41→	// Write Go source
    42→	goFile := filepath.Join(tmpDir, "main.go")
    43→	if err := os.WriteFile(goFile, []byte(goCode), 0644); err != nil {
    44→		t.Fatalf("failed to write Go file: %v", err)
    45→	}
    46→
    47→	// Compile
    48→	binFile := filepath.Join(tmpDir, "main")
    49→	compileCmd := exec.Command("go", "build", "-o", binFile, goFile)
    50→	compileOutput, err := compileCmd.CombinedOutput()
    51→	if err != nil {
    52→		t.Fatalf("compilation failed: %v\nOutput: %s\nGo code:\n%s", err, compileOutput, goCode)
    53→	}
    54→
    55→	// Run
    56→	runCmd := exec.Command(binFile)
    57→	output, err := runCmd.CombinedOutput()
    58→	if err != nil {
    59→		t.Fatalf("execution failed: %v\nOutput: %s", err, output)
    60→	}
    61→
    62→	return string(output)
    63→}
    64→
    65→// transpileCompileRunFromFile takes an occam file path, preprocesses it,
    66→// then transpiles, compiles, and runs.
    67→func transpileCompileRunFromFile(t *testing.T, mainFile string, includePaths []string) string {
    68→	t.Helper()
    69→
    70→	pp := preproc.New(preproc.WithIncludePaths(includePaths))
    71→	expanded, err := pp.ProcessFile(mainFile)
    72→	if err != nil {
    73→		t.Fatalf("preprocessor error: %v", err)
    74→	}
    75→	if len(pp.Errors()) > 0 {
    76→		for _, e := range pp.Errors() {
    77→			t.Errorf("preprocessor warning: %s", e)
    78→		}
    79→	}
    80→
    81→	return transpileCompileRun(t, expanded)
    82→}
    83→

Now let me look at the existing phase2 and types tests for patterns, and also the codegen to understand how intrinsics and RETYPES are generated.

Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/codegen/e2e_phase2_test.go"
}
     1→package codegen
     2→
     3→import (
     4→	"os"
     5→	"os/exec"
     6→	"path/filepath"
     7→	"testing"
     8→
     9→	"github.com/codeassociates/occam2go/lexer"
    10→	"github.com/codeassociates/occam2go/parser"
    11→)
    12→
    13→func TestE2E_UntypedValAbbreviation(t *testing.T) {
    14→	occam := `SEQ
    15→  VAL x IS 42 :
    16→  print.int(x)
    17→`
    18→	output := transpileCompileRun(t, occam)
    19→	expected := "42\n"
    20→	if output != expected {
    21→		t.Errorf("expected %q, got %q", expected, output)
    22→	}
    23→}
    24→
    25→func TestE2E_ArrayLiteralIndexing(t *testing.T) {
    26→	occam := `SEQ
    27→  VAL arr IS [10, 20, 30] :
    28→  print.int(arr[1])
    29→`
    30→	output := transpileCompileRun(t, occam)
    31→	expected := "20\n"
    32→	if output != expected {
    33→		t.Errorf("expected %q, got %q", expected, output)
    34→	}
    35→}
    36→
    37→func TestE2E_MultiLineBooleanIF(t *testing.T) {
    38→	occam := `SEQ
    39→  INT x:
    40→  x := 1
    41→  IF
    42→    (x > 0) AND
    43→      (x < 10)
    44→      print.int(x)
    45→    TRUE
    46→      print.int(0)
    47→`
    48→	output := transpileCompileRun(t, occam)
    49→	expected := "1\n"
    50→	if output != expected {
    51→		t.Errorf("expected %q, got %q", expected, output)
    52→	}
    53→}
    54→
    55→func TestE2E_CAUSEERROR(t *testing.T) {
    56→	occamSource := `PROC main()
    57→  CAUSEERROR()
    58→:
    59→`
    60→	// Transpile
    61→	l := lexer.New(occamSource)
    62→	p := parser.New(l)
    63→	program := p.ParseProgram()
    64→
    65→	if len(p.Errors()) > 0 {
    66→		for _, err := range p.Errors() {
    67→			t.Errorf("parser error: %s", err)
    68→		}
    69→		t.FailNow()
    70→	}
    71→
    72→	gen := New()
    73→	goCode := gen.Generate(program)
    74→
    75→	// Create temp directory
    76→	tmpDir, err := os.MkdirTemp("", "occam2go-test-*")
    77→	if err != nil {
    78→		t.Fatalf("failed to create temp dir: %v", err)
    79→	}
    80→	defer os.RemoveAll(tmpDir)
    81→
    82→	// Write Go source
    83→	goFile := filepath.Join(tmpDir, "main.go")
    84→	if err := os.WriteFile(goFile, []byte(goCode), 0644); err != nil {
    85→		t.Fatalf("failed to write Go file: %v", err)
    86→	}
    87→
    88→	// Compile
    89→	binFile := filepath.Join(tmpDir, "main")
    90→	compileCmd := exec.Command("go", "build", "-o", binFile, goFile)
    91→	compileOutput, err := compileCmd.CombinedOutput()
    92→	if err != nil {
    93→		t.Fatalf("compilation failed: %v\nOutput: %s\nGo code:\n%s", err, compileOutput, goCode)
    94→	}
    95→
    96→	// Run — expect non-zero exit code (panic)
    97→	runCmd := exec.Command(binFile)
    98→	err = runCmd.Run()
    99→	if err == nil {
   100→		t.Fatalf("expected CAUSEERROR to cause a non-zero exit, but program exited successfully")
   101→	}
   102→}
   103→

Now let me look at how intrinsics and RETYPES are handled in the codegen.

Grep
{
  "pattern": "LONGPROD|LONGDIV|LONGSUM|LONGDIFF|NORMALISE|SHIFTRIGHT|SHIFTLEFT",
  "path": "/home/david/projects/code-associates/occam2go/codegen",
  "output_mode": "files_with_matches"
}
Found 1 file
codegen/codegen.go
Grep
{
  "pattern": "LONGPROD|LONGDIV|LONGSUM|LONGDIFF|NORMALISE|SHIFTRIGHT|SHIFTLEFT",
  "path": "/home/david/projects/code-associates/occam2go/codegen/codegen.go",
  "output_mode": "content",
  "-n": true,
  "context": 3
}
44-
45-// Transputer intrinsic function names
46-var transpIntrinsics = map[string]bool{
47:	"LONGPROD":   true,
48:	"LONGDIV":    true,
49:	"LONGSUM":    true,
50:	"LONGDIFF":   true,
51:	"NORMALISE":  true,
52:	"SHIFTRIGHT": true,
53:	"SHIFTLEFT":  true,
54-}
55-
56-// Built-in print procedures
--
2571-// These implement 32-bit transputer semantics using uint32/uint64 arithmetic.
2572-func (g *Generator) emitIntrinsicHelpers() {
2573-	g.writeLine("// Transputer intrinsic helper functions")
2574:	g.writeLine("func _LONGPROD(a, b, c int) (int, int) {")
2575-	g.writeLine("\tr := uint64(uint32(a))*uint64(uint32(b)) + uint64(uint32(c))")
2576-	g.writeLine("\treturn int(int32(uint32(r >> 32))), int(int32(uint32(r)))")
2577-	g.writeLine("}")
2578-	g.writeLine("")
2579:	g.writeLine("func _LONGDIV(hi, lo, divisor int) (int, int) {")
2580-	g.writeLine("\tn := (uint64(uint32(hi)) << 32) | uint64(uint32(lo))")
2581-	g.writeLine("\td := uint64(uint32(divisor))")
2582:	g.writeLine("\tif d == 0 { panic(\"LONGDIV: division by zero\") }")
2583-	g.writeLine("\treturn int(int32(uint32(n / d))), int(int32(uint32(n % d)))")
2584-	g.writeLine("}")
2585-	g.writeLine("")
2586:	g.writeLine("func _LONGSUM(a, b, carry int) (int, int) {")
2587-	g.writeLine("\tr := uint64(uint32(a)) + uint64(uint32(b)) + uint64(uint32(carry))")
2588-	g.writeLine("\treturn int(int32(uint32(r >> 32))), int(int32(uint32(r)))")
2589-	g.writeLine("}")
2590-	g.writeLine("")
2591:	g.writeLine("func _LONGDIFF(a, b, borrow int) (int, int) {")
2592-	g.writeLine("\tr := uint64(uint32(a)) - uint64(uint32(b)) - uint64(uint32(borrow))")
2593-	g.writeLine("\tif uint32(a) >= uint32(b)+uint32(borrow) { return 0, int(int32(uint32(r))) }")
2594-	g.writeLine("\treturn 1, int(int32(uint32(r)))")
2595-	g.writeLine("}")
2596-	g.writeLine("")
2597:	g.writeLine("func _NORMALISE(hi, lo int) (int, int, int) {")
2598-	g.writeLine("\tv := (uint64(uint32(hi)) << 32) | uint64(uint32(lo))")
2599-	g.writeLine("\tif v == 0 { return 64, 0, 0 }")
2600-	g.writeLine("\tn := bits.LeadingZeros64(v)")
--
2602-	g.writeLine("\treturn n, int(int32(uint32(v >> 32))), int(int32(uint32(v)))")
2603-	g.writeLine("}")
2604-	g.writeLine("")
2605:	g.writeLine("func _SHIFTRIGHT(hi, lo, n int) (int, int) {")
2606-	g.writeLine("\tv := (uint64(uint32(hi)) << 32) | uint64(uint32(lo))")
2607-	g.writeLine("\tv >>= uint(uint32(n))")
2608-	g.writeLine("\treturn int(int32(uint32(v >> 32))), int(int32(uint32(v)))")
2609-	g.writeLine("}")
2610-	g.writeLine("")
2611:	g.writeLine("func _SHIFTLEFT(hi, lo, n int) (int, int) {")
2612-	g.writeLine("\tv := (uint64(uint32(hi)) << 32) | uint64(uint32(lo))")
2613-	g.writeLine("\tv <<= uint(uint32(n))")
2614-	g.writeLine("\treturn int(int32(uint32(v >> 32))), int(int32(uint32(v)))")
Grep
{
  "pattern": "RETYPES|generateRetypes",
  "path": "/home/david/projects/code-associates/occam2go/codegen/codegen.go",
  "output_mode": "content",
  "-n": true,
  "context": 5
}
34-	recordVars map[string]string // variable name → record type name
35-
36-	/ Nesting level: 0 = package level, >0 = inside a function
37-	nestingLevel int
38-
39:	// RETYPES parameter renames: when a RETYPES declaration shadows a
40:	// parameter (e.g. VAL INT X RETYPES X :), the parameter is renamed
41-	/ in the signature so := can create a new variable with the original name.
42-	retypesRenames map[string]string
43-}
44-
45-// Transputer intrinsic function names
--
177-			} else {
178-				mainStatements = append(mainStatements, stmt)
179-			}
180-		case *ast.RetypesDecl:
181-			_ = s
182:			// RETYPES declarations are local to functions, not package-level
183-			mainStatements = append(mainStatements, stmt)
184-		default:
185-			mainStatements = append(mainStatements, stmt)
186-		}
187-	}
--
1008-	case *ast.Abbreviation:
1009-		g.generateAbbreviation(s)
1010-	case *ast.MultiAssignment:
1011-		g.generateMultiAssignment(s)
1012-	case *ast.RetypesDecl:
1013:		g.generateRetypesDecl(s)
1014-	}
1015-}
1016-
1017-func (g *Generator) generateVarDecl(decl *ast.VarDecl) {
1018-	goType := g.occamTypeToGo(decl.Type)
--
1687-			}
1688-		}
1689-	}
1690-	g.refParams = newRefParams
1691-
1692:	// Scan proc body for RETYPES declarations that shadow parameters.
1693:	// When VAL INT X RETYPES X :, Go can't redeclare X in the same scope,
1694:	// so we rename the parameter (e.g. X → _rp_X) and let RETYPES declare the original name.
1695-	oldRenames := g.retypesRenames
1696-	g.retypesRenames = nil
1697-	paramNames := make(map[string]bool)
1698-	for _, p := range proc.Params {
1699-		paramNames[p.Name] = true
--
2324-		g.generateExpression(elem)
2325-	}
2326-	g.write("}")
2327-}
2328-
2329:// generateRetypesDecl emits code for a RETYPES declaration.
2330:// VAL INT X RETYPES X : — reinterpret float32/64 bits as int(s)
2331-// When source and target share the same name (shadowing a parameter), the parameter
2332-/ has been renamed in the signature (e.g. X → _rp_X) so we can use := with the
2333-// original name to create a new variable.
2334:func (g *Generator) generateRetypesDecl(r *ast.RetypesDecl) {
2335-	gName := goIdent(r.Name)
2336-	gSource := goIdent(r.Source)
2337:	// If the parameter was renamed for RETYPES shadowing, use the renamed source
2338-	if renamed, ok := g.retypesRenames[r.Source]; ok {
2339-		gSource = renamed
2340-	}
2341-	if r.IsArray {
2342:		// VAL [2]INT X RETYPES X : — split float64 into two int32 words
2343-		tmpVar := fmt.Sprintf("_retmp%d", g.tmpCounter)
2344-		g.tmpCounter++
2345-		g.writeLine(fmt.Sprintf("%s := math.Float64bits(float64(%s))", tmpVar, gSource))
2346-		g.writeLine(fmt.Sprintf("%s := []int{int(int32(uint32(%s))), int(int32(uint32(%s >> 32)))}", gName, tmpVar, tmpVar))
2347-	} else {
2348:		// VAL INT X RETYPES X : — reinterpret float32 as int
2349-		g.writeLine(fmt.Sprintf("%s := int(int32(math.Float32bits(float32(%s))))", gName, gSource))
2350-	}
2351-}
2352-
2353-// containsIntrinsics checks if a statement tree contains transputer intrinsic calls.
--
2358-		}
2359-		return false
2360-	})
2361-}
2362-
2363:// containsRetypes checks if a statement tree contains RETYPES declarations.
2364-func (g *Generator) containsRetypes(stmt ast.Statement) bool {
2365-	switch s := stmt.(type) {
2366-	case *ast.RetypesDecl:
2367-		return true
2368-	case *ast.SeqBlock:

Now let me look at the proc/func and concurrency tests for more patterns, and also look at the existing types tests.

Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/codegen/e2e_proc_func_test.go"
}
     1→package codegen
     2→
     3→import "testing"
     4→
     5→func TestE2E_Procedure(t *testing.T) {
     6→	occam := `PROC double(VAL INT x, INT result)
     7→  SEQ
     8→    result := x * 2
     9→
    10→SEQ
    11→  INT n, doubled:
    12→  n := 21
    13→  double(n, doubled)
    14→  print.int(doubled)
    15→`
    16→	output := transpileCompileRun(t, occam)
    17→	expected := "42\n"
    18→	if output != expected {
    19→		t.Errorf("expected %q, got %q", expected, output)
    20→	}
    21→}
    22→
    23→func TestE2E_FunctionIS(t *testing.T) {
    24→	occam := `INT FUNCTION square(VAL INT x)
    25→  IS x * x
    26→
    27→SEQ
    28→  INT n:
    29→  n := square(7)
    30→  print.int(n)
    31→`
    32→	output := transpileCompileRun(t, occam)
    33→	expected := "49\n"
    34→	if output != expected {
    35→		t.Errorf("expected %q, got %q", expected, output)
    36→	}
    37→}
    38→
    39→func TestE2E_FunctionValof(t *testing.T) {
    40→	occam := `INT FUNCTION max(VAL INT a, VAL INT b)
    41→  INT result:
    42→  VALOF
    43→    IF
    44→      a > b
    45→        result := a
    46→      TRUE
    47→        result := b
    48→    RESULT result
    49→
    50→SEQ
    51→  print.int(max(10, 20))
    52→`
    53→	output := transpileCompileRun(t, occam)
    54→	expected := "20\n"
    55→	if output != expected {
    56→		t.Errorf("expected %q, got %q", expected, output)
    57→	}
    58→}
    59→
    60→func TestE2E_FunctionInExpr(t *testing.T) {
    61→	occam := `INT FUNCTION double(VAL INT x)
    62→  IS x * 2
    63→
    64→SEQ
    65→  INT a:
    66→  a := double(3) + double(4)
    67→  print.int(a)
    68→`
    69→	output := transpileCompileRun(t, occam)
    70→	expected := "14\n"
    71→	if output != expected {
    72→		t.Errorf("expected %q, got %q", expected, output)
    73→	}
    74→}
    75→
    76→func TestE2E_ValAbbreviation(t *testing.T) {
    77→	occam := `SEQ
    78→  VAL INT x IS 42:
    79→  print.int(x)
    80→`
    81→	output := transpileCompileRun(t, occam)
    82→	expected := "42\n"
    83→	if output != expected {
    84→		t.Errorf("expected %q, got %q", expected, output)
    85→	}
    86→}
    87→
    88→func TestE2E_AbbreviationWithExpression(t *testing.T) {
    89→	occam := `SEQ
    90→  INT a:
    91→  a := 10
    92→  VAL INT b IS (a + 5):
    93→  print.int(b)
    94→`
    95→	output := transpileCompileRun(t, occam)
    96→	expected := "15\n"
    97→	if output != expected {
    98→		t.Errorf("expected %q, got %q", expected, output)
    99→	}
   100→}
   101→
   102→func TestE2E_ProcColonTerminator(t *testing.T) {
   103→	occam := `PROC double(VAL INT x, INT result)
   104→  SEQ
   105→    result := x * 2
   106→:
   107→
   108→SEQ
   109→  INT n, doubled:
   110→  n := 21
   111→  double(n, doubled)
   112→  print.int(doubled)
   113→`
   114→	output := transpileCompileRun(t, occam)
   115→	expected := "42\n"
   116→	if output != expected {
   117→		t.Errorf("expected %q, got %q", expected, output)
   118→	}
   119→}
   120→
   121→func TestE2E_FunctionISColonTerminator(t *testing.T) {
   122→	occam := `INT FUNCTION square(VAL INT x)
   123→  IS x * x
   124→:
   125→
   126→SEQ
   127→  INT n:
   128→  n := square(7)
   129→  print.int(n)
   130→`
   131→	output := transpileCompileRun(t, occam)
   132→	expected := "49\n"
   133→	if output != expected {
   134→		t.Errorf("expected %q, got %q", expected, output)
   135→	}
   136→}
   137→
   138→func TestE2E_FunctionValofColonTerminator(t *testing.T) {
   139→	occam := `INT FUNCTION max(VAL INT a, VAL INT b)
   140→  INT result:
   141→  VALOF
   142→    IF
   143→      a > b
   144→        result := a
   145→      TRUE
   146→        result := b
   147→    RESULT result
   148→:
   149→
   150→SEQ
   151→  print.int(max(10, 20))
   152→`
   153→	output := transpileCompileRun(t, occam)
   154→	expected := "20\n"
   155→	if output != expected {
   156→		t.Errorf("expected %q, got %q", expected, output)
   157→	}
   158→}
   159→
   160→func TestE2E_MultipleProcColonTerminator(t *testing.T) {
   161→	occam := `PROC add(VAL INT a, VAL INT b, INT result)
   162→  result := a + b
   163→:
   164→
   165→PROC double(VAL INT x, INT result)
   166→  SEQ
   167→    result := x * 2
   168→:
   169→
   170→SEQ
   171→  INT sum, doubled:
   172→  add(10, 11, sum)
   173→  double(sum, doubled)
   174→  print.int(doubled)
   175→`
   176→	output := transpileCompileRun(t, occam)
   177→	expected := "42\n"
   178→	if output != expected {
   179→		t.Errorf("expected %q, got %q", expected, output)
   180→	}
   181→}
   182→
   183→func TestE2E_MultiResultFunction(t *testing.T) {
   184→	occam := `INT, INT FUNCTION swap(VAL INT a, VAL INT b)
   185→  INT x, y:
   186→  VALOF
   187→    SEQ
   188→      x := b
   189→      y := a
   190→    RESULT x, y
   191→
   192→SEQ
   193→  INT p, q:
   194→  p, q := swap(10, 20)
   195→  print.int(p)
   196→  print.int(q)
   197→`
   198→	output := transpileCompileRun(t, occam)
   199→	expected := "20\n10\n"
   200→	if output != expected {
   201→		t.Errorf("expected %q, got %q", expected, output)
   202→	}
   203→}
   204→
   205→func TestE2E_MultiResultFunctionThreeValues(t *testing.T) {
   206→	occam := `INT, INT, INT FUNCTION rotate(VAL INT a, VAL INT b, VAL INT c)
   207→  INT x, y, z:
   208→  VALOF
   209→    SEQ
   210→      x := b
   211→      y := c
   212→      z := a
   213→    RESULT x, y, z
   214→
   215→SEQ
   216→  INT p, q, r:
   217→  p, q, r := rotate(1, 2, 3)
   218→  print.int(p)
   219→  print.int(q)
   220→  print.int(r)
   221→`
   222→	output := transpileCompileRun(t, occam)
   223→	expected := "2\n3\n1\n"
   224→	if output != expected {
   225→		t.Errorf("expected %q, got %q", expected, output)
   226→	}
   227→}
   228→
   229→func TestE2E_NonValAbbreviation(t *testing.T) {
   230→	occam := `SEQ
   231→  INT x:
   232→  x := 7
   233→  INT y IS x:
   234→  print.int(y)
   235→`
   236→	output := transpileCompileRun(t, occam)
   237→	expected := "7\n"
   238→	if output != expected {
   239→		t.Errorf("expected %q, got %q", expected, output)
   240→	}
   241→}
   242→
   243→func TestE2E_NestedProcClosure(t *testing.T) {
   244→	occam := `PROC outer(VAL INT n)
   245→  INT x:
   246→  PROC inner()
   247→    x := n * 2
   248→  :
   249→  SEQ
   250→    inner()
   251→    print.int(x)
   252→:
   253→
   254→SEQ
   255→  outer(21)
   256→`
   257→	output := transpileCompileRun(t, occam)
   258→	expected := "42\n"
   259→	if output != expected {
   260→		t.Errorf("expected %q, got %q", expected, output)
   261→	}
   262→}
   263→
   264→func TestE2E_NestedProcWithParams(t *testing.T) {
   265→	occam := `PROC outer()
   266→  INT result:
   267→  PROC add(VAL INT a, VAL INT b)
   268→    result := a + b
   269→  :
   270→  SEQ
   271→    add(10, 32)
   272→    print.int(result)
   273→:
   274→
   275→SEQ
   276→  outer()
   277→`
   278→	output := transpileCompileRun(t, occam)
   279→	expected := "42\n"
   280→	if output != expected {
   281→		t.Errorf("expected %q, got %q", expected, output)
   282→	}
   283→}
   284→
   285→func TestE2E_NestedFunctionIS(t *testing.T) {
   286→	occam := `PROC compute()
   287→  INT FUNCTION double(VAL INT x)
   288→    IS x * 2
   289→  SEQ
   290→    print.int(double(21))
   291→:
   292→
   293→SEQ
   294→  compute()
   295→`
   296→	output := transpileCompileRun(t, occam)
   297→	expected := "42\n"
   298→	if output != expected {
   299→		t.Errorf("expected %q, got %q", expected, output)
   300→	}
   301→}
   302→
   303→func TestE2E_NestedFunctionValof(t *testing.T) {
   304→	occam := `PROC compute()
   305→  INT FUNCTION max(VAL INT a, VAL INT b)
   306→    INT result:
   307→    VALOF
   308→      IF
   309→        a > b
   310→          result := a
   311→        TRUE
   312→          result := b
   313→      RESULT result
   314→  SEQ
   315→    print.int(max(42, 10))
   316→:
   317→
   318→SEQ
   319→  compute()
   320→`
   321→	output := transpileCompileRun(t, occam)
   322→	expected := "42\n"
   323→	if output != expected {
   324→		t.Errorf("expected %q, got %q", expected, output)
   325→	}
   326→}
   327→
   328→func TestE2E_MultipleNestedProcs(t *testing.T) {
   329→	occam := `PROC compute()
   330→  INT x:
   331→  PROC setten()
   332→    x := 10
   333→  :
   334→  PROC setthirtytwo()
   335→    x := 32
   336→  :
   337→  SEQ
   338→    setten()
   339→    INT a:
   340→    a := x
   341→    setthirtytwo()
   342→    print.int(a + x)
   343→:
   344→
   345→SEQ
   346→  compute()
   347→`
   348→	output := transpileCompileRun(t, occam)
   349→	expected := "42\n"
   350→	if output != expected {
   351→		t.Errorf("expected %q, got %q", expected, output)
   352→	}
   353→}
   354→
   355→func TestE2E_ProcLocalVarDecls(t *testing.T) {
   356→	occam := `PROC foo(VAL INT n)
   357→  INT x:
   358→  INT y:
   359→  SEQ
   360→    x := n
   361→    y := n * 2
   362→    print.int(x + y)
   363→:
   364→
   365→SEQ
   366→  foo(14)
   367→`
   368→	output := transpileCompileRun(t, occam)
   369→	expected := "42\n"
   370→	if output != expected {
   371→		t.Errorf("expected %q, got %q", expected, output)
   372→	}
   373→}
   374→

Now let me also check the concurrency test to understand the ALT pattern and see the existing types test for MOSTNEG/MOSTPOS.

Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/codegen/e2e_concurrency_test.go"
}
     1→package codegen
     2→
     3→import "testing"
     4→
     5→func TestE2E_PAR(t *testing.T) {
     6→	// Test that PAR executes both branches
     7→	// We can't guarantee order, but both should run
     8→	occam := `SEQ
     9→  INT x, y:
    10→  PAR
    11→    x := 10
    12→    y := 20
    13→  print.int(x + y)
    14→`
    15→	output := transpileCompileRun(t, occam)
    16→	expected := "30\n"
    17→	if output != expected {
    18→		t.Errorf("expected %q, got %q", expected, output)
    19→	}
    20→}
    21→
    22→func TestE2E_Channel(t *testing.T) {
    23→	// Test basic channel communication between parallel processes
    24→	occam := `SEQ
    25→  CHAN OF INT c:
    26→  INT result:
    27→  PAR
    28→    c ! 42
    29→    c ? result
    30→  print.int(result)
    31→`
    32→	output := transpileCompileRun(t, occam)
    33→	expected := "42\n"
    34→	if output != expected {
    35→		t.Errorf("expected %q, got %q", expected, output)
    36→	}
    37→}
    38→
    39→func TestE2E_ChannelExpression(t *testing.T) {
    40→	// Test sending an expression over a channel
    41→	occam := `SEQ
    42→  CHAN OF INT c:
    43→  INT x, result:
    44→  x := 10
    45→  PAR
    46→    c ! x * 2
    47→    c ? result
    48→  print.int(result)
    49→`
    50→	output := transpileCompileRun(t, occam)
    51→	expected := "20\n"
    52→	if output != expected {
    53→		t.Errorf("expected %q, got %q", expected, output)
    54→	}
    55→}
    56→
    57→func TestE2E_ChannelPingPong(t *testing.T) {
    58→	// Test two-way communication: send a value, double it, send back
    59→	occam := `SEQ
    60→  CHAN OF INT request:
    61→  CHAN OF INT response:
    62→  INT result:
    63→  PAR
    64→    SEQ
    65→      request ! 21
    66→      response ? result
    67→    SEQ
    68→      INT x:
    69→      request ? x
    70→      response ! x * 2
    71→  print.int(result)
    72→`
    73→	output := transpileCompileRun(t, occam)
    74→	expected := "42\n"
    75→	if output != expected {
    76→		t.Errorf("expected %q, got %q", expected, output)
    77→	}
    78→}
    79→
    80→func TestE2E_AltBasic(t *testing.T) {
    81→	// Test basic ALT: select from first ready channel
    82→	occam := `SEQ
    83→  CHAN OF INT c1:
    84→  CHAN OF INT c2:
    85→  INT result:
    86→  PAR
    87→    c1 ! 42
    88→    ALT
    89→      c1 ? result
    90→        print.int(result)
    91→      c2 ? result
    92→        print.int(result)
    93→`
    94→	output := transpileCompileRun(t, occam)
    95→	expected := "42\n"
    96→	if output != expected {
    97→		t.Errorf("expected %q, got %q", expected, output)
    98→	}
    99→}
   100→
   101→func TestE2E_AltSecondChannel(t *testing.T) {
   102→	// Test ALT selecting from second channel
   103→	occam := `SEQ
   104→  CHAN OF INT c1:
   105→  CHAN OF INT c2:
   106→  INT result:
   107→  PAR
   108→    c2 ! 99
   109→    ALT
   110→      c1 ? result
   111→        print.int(result)
   112→      c2 ? result
   113→        print.int(result)
   114→`
   115→	output := transpileCompileRun(t, occam)
   116→	expected := "99\n"
   117→	if output != expected {
   118→		t.Errorf("expected %q, got %q", expected, output)
   119→	}
   120→}
   121→
   122→func TestE2E_AltWithBody(t *testing.T) {
   123→	// Test ALT with computation in body
   124→	occam := `SEQ
   125→  CHAN OF INT c:
   126→  INT result:
   127→  PAR
   128→    c ! 10
   129→    ALT
   130→      c ? result
   131→        SEQ
   132→          result := result * 2
   133→          print.int(result)
   134→`
   135→	output := transpileCompileRun(t, occam)
   136→	expected := "20\n"
   137→	if output != expected {
   138→		t.Errorf("expected %q, got %q", expected, output)
   139→	}
   140→}
   141→
   142→func TestE2E_TimerRead(t *testing.T) {
   143→	// Test reading a timer: value should be positive (microseconds since epoch)
   144→	occam := `SEQ
   145→  TIMER tim:
   146→  INT t:
   147→  tim ? t
   148→  IF
   149→    t > 0
   150→      print.int(1)
   151→    TRUE
   152→      print.int(0)
   153→`
   154→	output := transpileCompileRun(t, occam)
   155→	expected := "1\n"
   156→	if output != expected {
   157→		t.Errorf("expected %q, got %q", expected, output)
   158→	}
   159→}
   160→
   161→func TestE2E_TimerAltTimeout(t *testing.T) {
   162→	// Test ALT with timer timeout: no channel is ready, so timer fires
   163→	occam := `SEQ
   164→  TIMER tim:
   165→  INT t:
   166→  tim ? t
   167→  CHAN OF INT c:
   168→  INT result:
   169→  result := 0
   170→  ALT
   171→    c ? result
   172→      result := 1
   173→    tim ? AFTER (t + 1000)
   174→      result := 2
   175→  print.int(result)
   176→`
   177→	output := transpileCompileRun(t, occam)
   178→	expected := "2\n"
   179→	if output != expected {
   180→		t.Errorf("expected %q, got %q", expected, output)
   181→	}
   182→}
   183→
   184→func TestE2E_ChanParam(t *testing.T) {
   185→	occam := `PROC sender(CHAN OF INT output)
   186→  output ! 42
   187→
   188→SEQ
   189→  CHAN OF INT c:
   190→  PAR
   191→    sender(c)
   192→    SEQ
   193→      INT x:
   194→      c ? x
   195→      print.int(x)
   196→`
   197→	output := transpileCompileRun(t, occam)
   198→	expected := "42\n"
   199→	if output != expected {
   200→		t.Errorf("expected %q, got %q", expected, output)
   201→	}
   202→}
   203→
   204→func TestE2E_ChanParamTwoWay(t *testing.T) {
   205→	occam := `PROC doubler(CHAN OF INT input, CHAN OF INT output)
   206→  SEQ
   207→    INT x:
   208→    input ? x
   209→    output ! x * 2
   210→
   211→SEQ
   212→  CHAN OF INT inCh:
   213→  CHAN OF INT outCh:
   214→  PAR
   215→    doubler(inCh, outCh)
   216→    SEQ
   217→      inCh ! 21
   218→      INT result:
   219→      outCh ? result
   220→      print.int(result)
   221→`
   222→	output := transpileCompileRun(t, occam)
   223→	expected := "42\n"
   224→	if output != expected {
   225→		t.Errorf("expected %q, got %q", expected, output)
   226→	}
   227→}
   228→
   229→func TestE2E_ChanDirParam(t *testing.T) {
   230→	occam := `PROC producer(CHAN OF INT output!)
   231→  output ! 42
   232→
   233→PROC consumer(CHAN OF INT input?)
   234→  SEQ
   235→    INT x:
   236→    input ? x
   237→    print.int(x)
   238→
   239→SEQ
   240→  CHAN OF INT c:
   241→  PAR
   242→    producer(c)
   243→    consumer(c)
   244→`
   245→	output := transpileCompileRun(t, occam)
   246→	expected := "42\n"
   247→	if output != expected {
   248→		t.Errorf("expected %q, got %q", expected, output)
   249→	}
   250→}
   251→
   252→func TestE2EChanShorthand(t *testing.T) {
   253→	occam := `SEQ
   254→  CHAN INT c:
   255→  INT result:
   256→  PAR
   257→    c ! 42
   258→    c ? result
   259→  print.int(result)
   260→`
   261→	output := transpileCompileRun(t, occam)
   262→	expected := "42\n"
   263→	if output != expected {
   264→		t.Errorf("expected %q, got %q", expected, output)
   265→	}
   266→}
   267→
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/codegen/e2e_types_test.go"
}
     1→package codegen
     2→
     3→import "testing"
     4→
     5→func TestE2E_TypeConversionIntFromByte(t *testing.T) {
     6→	occam := `SEQ
     7→  BYTE b:
     8→  b := 65
     9→  INT x:
    10→  x := INT b
    11→  print.int(x)
    12→`
    13→	output := transpileCompileRun(t, occam)
    14→	expected := "65\n"
    15→	if output != expected {
    16→		t.Errorf("expected %q, got %q", expected, output)
    17→	}
    18→}
    19→
    20→func TestE2E_TypeConversionByteFromInt(t *testing.T) {
    21→	occam := `SEQ
    22→  INT n:
    23→  n := 72
    24→  BYTE b:
    25→  b := BYTE n
    26→  print.int(INT b)
    27→`
    28→	output := transpileCompileRun(t, occam)
    29→	expected := "72\n"
    30→	if output != expected {
    31→		t.Errorf("expected %q, got %q", expected, output)
    32→	}
    33→}
    34→
    35→func TestE2E_TypeConversionInExpression(t *testing.T) {
    36→	occam := `SEQ
    37→  BYTE b:
    38→  b := 10
    39→  INT x:
    40→  x := INT b + 1
    41→  print.int(x)
    42→`
    43→	output := transpileCompileRun(t, occam)
    44→	expected := "11\n"
    45→	if output != expected {
    46→		t.Errorf("expected %q, got %q", expected, output)
    47→	}
    48→}
    49→
    50→func TestE2E_Real32VarDecl(t *testing.T) {
    51→	occam := `SEQ
    52→  REAL32 x:
    53→  x := REAL32 5
    54→  print.int(INT x)
    55→`
    56→	output := transpileCompileRun(t, occam)
    57→	expected := "5\n"
    58→	if output != expected {
    59→		t.Errorf("expected %q, got %q", expected, output)
    60→	}
    61→}
    62→
    63→func TestE2E_Real64VarDecl(t *testing.T) {
    64→	occam := `SEQ
    65→  REAL64 x:
    66→  x := REAL64 10
    67→  print.int(INT x)
    68→`
    69→	output := transpileCompileRun(t, occam)
    70→	expected := "10\n"
    71→	if output != expected {
    72→		t.Errorf("expected %q, got %q", expected, output)
    73→	}
    74→}
    75→
    76→func TestE2E_Real32ToReal64Conversion(t *testing.T) {
    77→	occam := `SEQ
    78→  REAL32 a:
    79→  a := REAL32 7
    80→  REAL64 b:
    81→  b := REAL64 a
    82→  print.int(INT b)
    83→`
    84→	output := transpileCompileRun(t, occam)
    85→	expected := "7\n"
    86→	if output != expected {
    87→		t.Errorf("expected %q, got %q", expected, output)
    88→	}
    89→}
    90→
    91→func TestE2E_Real32Array(t *testing.T) {
    92→	occam := `SEQ
    93→  [3]REAL32 arr:
    94→  arr[0] := REAL32 10
    95→  arr[1] := REAL32 20
    96→  arr[2] := REAL32 30
    97→  INT sum:
    98→  sum := (INT arr[0]) + (INT arr[1]) + (INT arr[2])
    99→  print.int(sum)
   100→`
   101→	output := transpileCompileRun(t, occam)
   102→	expected := "60\n"
   103→	if output != expected {
   104→		t.Errorf("expected %q, got %q", expected, output)
   105→	}
   106→}
   107→
   108→func TestE2E_HexLiteral(t *testing.T) {
   109→	occam := `SEQ
   110→  INT x:
   111→  x := #FF
   112→  print.int(x)
   113→`
   114→	output := transpileCompileRun(t, occam)
   115→	expected := "255\n"
   116→	if output != expected {
   117→		t.Errorf("expected %q, got %q", expected, output)
   118→	}
   119→}
   120→
   121→func TestE2E_HexLiteralInExpression(t *testing.T) {
   122→	occam := `SEQ
   123→  INT x:
   124→  x := #0A + #14
   125→  print.int(x)
   126→`
   127→	output := transpileCompileRun(t, occam)
   128→	expected := "30\n"
   129→	if output != expected {
   130→		t.Errorf("expected %q, got %q", expected, output)
   131→	}
   132→}
   133→
   134→func TestE2E_BitwiseAnd(t *testing.T) {
   135→	occam := `SEQ
   136→  INT x:
   137→  x := 12 /\ 10
   138→  print.int(x)
   139→`
   140→	output := transpileCompileRun(t, occam)
   141→	expected := "8\n"
   142→	if output != expected {
   143→		t.Errorf("expected %q, got %q", expected, output)
   144→	}
   145→}
   146→
   147→func TestE2E_BitwiseOr(t *testing.T) {
   148→	occam := `SEQ
   149→  INT x:
   150→  x := 12 \/ 10
   151→  print.int(x)
   152→`
   153→	output := transpileCompileRun(t, occam)
   154→	expected := "14\n"
   155→	if output != expected {
   156→		t.Errorf("expected %q, got %q", expected, output)
   157→	}
   158→}
   159→
   160→func TestE2E_BitwiseXor(t *testing.T) {
   161→	occam := `SEQ
   162→  INT x:
   163→  x := 12 >< 10
   164→  print.int(x)
   165→`
   166→	output := transpileCompileRun(t, occam)
   167→	expected := "6\n"
   168→	if output != expected {
   169→		t.Errorf("expected %q, got %q", expected, output)
   170→	}
   171→}
   172→
   173→func TestE2E_BitwiseNot(t *testing.T) {
   174→	occam := `SEQ
   175→  INT x:
   176→  x := ~ 0
   177→  print.int(x)
   178→`
   179→	output := transpileCompileRun(t, occam)
   180→	expected := "-1\n"
   181→	if output != expected {
   182→		t.Errorf("expected %q, got %q", expected, output)
   183→	}
   184→}
   185→
   186→func TestE2E_LeftShift(t *testing.T) {
   187→	occam := `SEQ
   188→  INT x:
   189→  x := 1 << 4
   190→  print.int(x)
   191→`
   192→	output := transpileCompileRun(t, occam)
   193→	expected := "16\n"
   194→	if output != expected {
   195→		t.Errorf("expected %q, got %q", expected, output)
   196→	}
   197→}
   198→
   199→func TestE2E_RightShift(t *testing.T) {
   200→	occam := `SEQ
   201→  INT x:
   202→  x := 16 >> 2
   203→  print.int(x)
   204→`
   205→	output := transpileCompileRun(t, occam)
   206→	expected := "4\n"
   207→	if output != expected {
   208→		t.Errorf("expected %q, got %q", expected, output)
   209→	}
   210→}
   211→
   212→func TestE2E_AfterExpression(t *testing.T) {
   213→	// Test AFTER as a boolean expression in IF
   214→	occam := `SEQ
   215→  INT t1, t2:
   216→  t1 := 100
   217→  t2 := 200
   218→  IF
   219→    t2 AFTER t1
   220→      print.int(1)
   221→    TRUE
   222→      print.int(0)
   223→`
   224→	output := transpileCompileRun(t, occam)
   225→	expected := "1\n"
   226→	if output != expected {
   227→		t.Errorf("expected %q, got %q", expected, output)
   228→	}
   229→}
   230→
   231→func TestE2E_ByteLiteral(t *testing.T) {
   232→	occam := `SEQ
   233→  BYTE x:
   234→  x := 'A'
   235→  print.int(INT x)
   236→`
   237→	output := transpileCompileRun(t, occam)
   238→	expected := "65\n"
   239→	if output != expected {
   240→		t.Errorf("expected %q, got %q", expected, output)
   241→	}
   242→}
   243→
   244→func TestE2E_ByteLiteralEscape(t *testing.T) {
   245→	occam := `SEQ
   246→  BYTE x:
   247→  x := '*n'
   248→  print.int(INT x)
   249→`
   250→	output := transpileCompileRun(t, occam)
   251→	expected := "10\n"
   252→	if output != expected {
   253→		t.Errorf("expected %q, got %q", expected, output)
   254→	}
   255→}
   256→
   257→func TestE2E_MostNegInt(t *testing.T) {
   258→	occam := `SEQ
   259→  INT x:
   260→  x := MOSTNEG INT
   261→  BOOL neg:
   262→  IF
   263→    x < 0
   264→      neg := TRUE
   265→    TRUE
   266→      neg := FALSE
   267→  print.bool(neg)
   268→`
   269→	output := transpileCompileRun(t, occam)
   270→	expected := "true\n"
   271→	if output != expected {
   272→		t.Errorf("expected %q, got %q", expected, output)
   273→	}
   274→}
   275→
   276→func TestE2E_MostPosInt(t *testing.T) {
   277→	occam := `SEQ
   278→  INT x:
   279→  x := MOSTPOS INT
   280→  BOOL pos:
   281→  IF
   282→    x > 0
   283→      pos := TRUE
   284→    TRUE
   285→      pos := FALSE
   286→  print.bool(pos)
   287→`
   288→	output := transpileCompileRun(t, occam)
   289→	expected := "true\n"
   290→	if output != expected {
   291→		t.Errorf("expected %q, got %q", expected, output)
   292→	}
   293→}
   294→
   295→func TestE2E_MostNegByte(t *testing.T) {
   296→	occam := `SEQ
   297→  BYTE x:
   298→  x := MOSTNEG BYTE
   299→  print.int(INT x)
   300→`
   301→	output := transpileCompileRun(t, occam)
   302→	expected := "0\n"
   303→	if output != expected {
   304→		t.Errorf("expected %q, got %q", expected, output)
   305→	}
   306→}
   307→
   308→func TestE2E_MostPosByte(t *testing.T) {
   309→	occam := `SEQ
   310→  BYTE x:
   311→  x := MOSTPOS BYTE
   312→  print.int(INT x)
   313→`
   314→	output := transpileCompileRun(t, occam)
   315→	expected := "255\n"
   316→	if output != expected {
   317→		t.Errorf("expected %q, got %q", expected, output)
   318→	}
   319→}
   320→
   321→func TestE2E_MostNegInExpression(t *testing.T) {
   322→	// Test MOSTNEG INT used in comparison (like utils.occ does)
   323→	occam := `SEQ
   324→  INT n:
   325→  n := MOSTNEG INT
   326→  IF
   327→    n = (MOSTNEG INT)
   328→      print.int(1)
   329→    TRUE
   330→      print.int(0)
   331→`
   332→	output := transpileCompileRun(t, occam)
   333→	expected := "1\n"
   334→	if output != expected {
   335→		t.Errorf("expected %q, got %q", expected, output)
   336→	}
   337→}
   338→
   339→func TestE2E_CheckedArithmeticPLUS(t *testing.T) {
   340→	occam := `SEQ
   341→  INT x:
   342→  SEQ
   343→    x := 3 PLUS 4
   344→    print.int(x)
   345→`
   346→	output := transpileCompileRun(t, occam)
   347→	expected := "7\n"
   348→	if output != expected {
   349→		t.Errorf("expected %q, got %q", expected, output)
   350→	}
   351→}
   352→
   353→func TestE2E_CheckedArithmeticMINUS(t *testing.T) {
   354→	occam := `SEQ
   355→  INT x:
   356→  SEQ
   357→    x := 10 MINUS 3
   358→    print.int(x)
   359→`
   360→	output := transpileCompileRun(t, occam)
   361→	expected := "7\n"
   362→	if output != expected {
   363→		t.Errorf("expected %q, got %q", expected, output)
   364→	}
   365→}
   366→
   367→func TestE2E_CheckedArithmeticTIMES(t *testing.T) {
   368→	occam := `SEQ
   369→  INT x:
   370→  SEQ
   371→    x := 6 TIMES 7
   372→    print.int(x)
   373→`
   374→	output := transpileCompileRun(t, occam)
   375→	expected := "42\n"
   376→	if output != expected {
   377→		t.Errorf("expected %q, got %q", expected, output)
   378→	}
   379→}
   380→
   381→func TestE2E_CheckedArithmeticWrapping(t *testing.T) {
   382→	// MOSTPOS INT PLUS 1 should wrap to MOSTNEG INT (modular arithmetic)
   383→	// Use a variable so Go doesn't detect constant overflow at compile time
   384→	occam := `SEQ
   385→  INT x:
   386→  SEQ
   387→    x := MOSTPOS INT
   388→    x := x PLUS 1
   389→    BOOL neg:
   390→    IF
   391→      x = (MOSTNEG INT)
   392→        neg := TRUE
   393→      TRUE
   394→        neg := FALSE
   395→    print.bool(neg)
   396→`
   397→	output := transpileCompileRun(t, occam)
   398→	expected := "true\n"
   399→	if output != expected {
   400→		t.Errorf("expected %q, got %q", expected, output)
   401→	}
   402→}
   403→

Good, I now have a clear picture of the patterns. Let me also quickly check how print.string is handled in codegen, and how the RESULT param works.

Grep
{
  "pattern": "print\\.(string|newline|nl)",
  "path": "/home/david/projects/code-associates/occam2go/codegen/codegen.go",
  "output_mode": "content",
  "-n": true,
  "context": 3
}
56-// Built-in print procedures
57-var printBuiltins = map[string]bool{
58-	"print.int":     true,
59:	"print.string":  true,
60-	"print.bool":    true,
61:	"print.newline": true,
62-}
63-
64-// New creates a new code generator
--
1951-	g.builder.WriteString(strings.Repeat("\t", g.indent))
1952-
1953-	switch call.Name {
1954:	case "print.int", "print.string", "print.bool":
1955-		g.write("fmt.Println(")
1956-		if len(call.Args) > 0 {
1957-			g.generateExpression(call.Args[0])
1958-		}
1959-		g.write(")")
1960:	case "print.newline":
1961-		g.write("fmt.Println()")
1962-	}
1963-
Grep
{
  "pattern": "RESULT|ResultQualifier",
  "path": "/home/david/projects/code-associates/occam2go/ast/ast.go",
  "output_mode": "content",
  "-n": true,
  "context": 2
}
171-	Params      []ProcParam
172-	Body        []Statement    // local decls + body statements (VALOF form), empty for IS form
173:	ResultExprs []Expression   // return expressions (from IS or RESULT)
174-}
175-
Grep
{
  "pattern": "IsResult|RESULT.*qual",
  "path": "/home/david/projects/code-associates/occam2go/ast/ast.go",
  "output_mode": "content",
  "-n": true,
  "context": 5
}
No matches found
Grep
{
  "pattern": "type ProcParam",
  "path": "/home/david/projects/code-associates/occam2go/ast/ast.go",
  "output_mode": "content",
  "-n": true,
  "context": 15
}
129-func (s *Stop) statementNode()       {}
130-func (s *Stop) TokenLiteral() string { return s.Token.Literal }
131-
132-// ProcDecl represents a procedure declaration
133-type ProcDecl struct {
134-	Token  lexer.Token // the PROC token
135-	Name   string
136-	Params []ProcParam
137-	Body   []Statement // local declarations + body process
138-}
139-
140-func (p *ProcDecl) statementNode()       {}
141-func (p *ProcDecl) TokenLiteral() string { return p.Token.Literal }
142-
143-// ProcParam represents a procedure parameter
144:type ProcParam struct {
145-	IsVal        bool   // VAL parameter (pass by value)
146-	Type         string // INT, BYTE, BOOL, etc.
147-	Name         string
148-	IsChan       bool   // true if this is a CHAN OF <type> parameter
149-	IsChanArray  bool   // true for []CHAN OF TYPE params
150-	IsOpenArray  bool   // true for []TYPE params (open array)
151-	ChanElemType string // element type when IsChan (e.g., "INT")
152-	ChanDir      string // "?" for input, "!" for output, "" for bidirectional
153-	ArraySize    string // non-empty for fixed-size array params like [2]INT
154-}
155-
156-// ProcCall represents a procedure call
157-type ProcCall struct {
158-	Token lexer.Token // the procedure name token
159-	Name  string

I see there's no IsResult field — RESULT params are treated the same as non-VAL params (both become pointer params). Let me check how the parser handles them.

Grep
{
  "pattern": "RESULT.*param|parseParam.*RESULT",
  "path": "/home/david/projects/code-associates/occam2go/parser/parser.go",
  "output_mode": "content",
  "-n": true,
  "context": 3
}
2001-			p.nextToken()
2002-		}
2003-
2004:		// Check for RESULT keyword (output-only parameter — maps to pointer like non-VAL)
2005-		if p.curTokenIs(lexer.RESULT) {
2006:			// RESULT is semantically like non-VAL (pointer param), just skip it
2007-			p.nextToken()
2008-		}
2009-

Good. Now I have all the context I need. Let me start writing the new test files. I'll create them in parallel batches.

📝 Write e2e_intrinsics_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_intrinsics_test.go
package codegen

import "testing"

func TestE2E_LONGPROD(t *testing.T) {
	// LONGPROD(a, b, c) = a*b+c as 64-bit, returns (hi, lo)
	// 100000 * 100000 + 0 = 10000000000
	// 10000000000 = 0x2_540B_E400
	// hi = 2, lo = 0x540BE400 = 1409286144
	occam := `PROC main()
  INT hi, lo:
  SEQ
    hi, lo := LONGPROD(100000, 100000, 0)
    print.int(hi)
    print.int(lo)
:
`
	output := transpileCompileRun(t, occam)
	expected := "2\n1409286144\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_LONGPRODWithCarry(t *testing.T) {
	// LONGPROD(a, b, carry) = a*b+carry
	// 3 * 4 + 5 = 17, fits in lo word
	occam := `PROC main()
  INT hi, lo:
  SEQ
    hi, lo := LONGPROD(3, 4, 5)
    print.int(hi)
    print.int(lo)
:
`
	output := transpileCompileRun(t, occam)
	expected := "0\n17\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_LONGDIV(t *testing.T) {
	// LONGDIV(hi, lo, divisor) divides (hi:lo) by divisor → (quotient, remainder)
	// (0:42) / 5 = quotient 8, remainder 2
	occam := `PROC main()
  INT quot, rem:
  SEQ
    quot, rem := LONGDIV(0, 42, 5)
    print.int(quot)
    print.int(rem)
:
`
	output := transpileCompileRun(t, occam)
	expected := "8\n2\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_LONGDIVLargeValue(t *testing.T) {
	// (2:1409286144) / 100000 = 10000000000 / 100000 = 100000
	// Use the result from LONGPROD to roundtrip
	occam := `PROC main()
  INT hi, lo, quot, rem:
  SEQ
    hi, lo := LONGPROD(100000, 100000, 0)
    quot, rem := LONGDIV(hi, lo, 100000)
    print.int(quot)
    print.int(rem)
:
`
	output := transpileCompileRun(t, occam)
	expected := "100000\n0\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_LONGSUM(t *testing.T) {
	// LONGSUM(a, b, carry) = a+b+carry as 64-bit → (carry_out, sum)
	// 10 + 20 + 0 = 30, no carry
	occam := `PROC main()
  INT carry, sum:
  SEQ
    carry, sum := LONGSUM(10, 20, 0)
    print.int(carry)
    print.int(sum)
:
`
	output := transpileCompileRun(t, occam)
	expected := "0\n30\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_LONGSUMOverflow(t *testing.T) {
	// LONGSUM with overflow: MOSTPOS INT + MOSTPOS INT + 0
	// In uint32: 0x7FFFFFFF + 0x7FFFFFFF = 0xFFFFFFFE → carry=0, result = -2 (signed)
	// Actually for 32-bit: 2147483647+2147483647 = 4294967294 = 0xFFFFFFFE
	// hi=0 (no carry past 32 bits), lo = int32(0xFFFFFFFE) = -2
	occam := `PROC main()
  INT carry, sum:
  SEQ
    carry, sum := LONGSUM(MOSTPOS INT, MOSTPOS INT, 0)
    print.int(carry)
    print.int(sum)
:
`
	output := transpileCompileRun(t, occam)
	expected := "0\n-2\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_LONGDIFF(t *testing.T) {
	// LONGDIFF(a, b, borrow) = a-b-borrow → (borrow_out, diff)
	// 30 - 10 - 0 = 20, no borrow
	occam := `PROC main()
  INT borrow, diff:
  SEQ
    borrow, diff := LONGDIFF(30, 10, 0)
    print.int(borrow)
    print.int(diff)
:
`
	output := transpileCompileRun(t, occam)
	expected := "0\n20\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_LONGDIFFBorrow(t *testing.T) {
	// LONGDIFF(10, 30, 0): 10-30 = underflow
	// uint32(10) - uint32(30) = wraps → borrow=1
	// uint32 result: 0xFFFFFFEC → int32 = -20
	occam := `PROC main()
  INT borrow, diff:
  SEQ
    borrow, diff := LONGDIFF(10, 30, 0)
    print.int(borrow)
    print.int(diff)
:
`
	output := transpileCompileRun(t, occam)
	expected := "1\n-20\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_NORMALISE(t *testing.T) {
	// NORMALISE(hi, lo) shifts left until MSB is set
	// NORMALISE(0, 1) — value is 1, needs 63 left shifts to set bit 63
	occam := `PROC main()
  INT places, nhi, nlo:
  SEQ
    places, nhi, nlo := NORMALISE(0, 1)
    print.int(places)
:
`
	output := transpileCompileRun(t, occam)
	expected := "63\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_NORMALISEZero(t *testing.T) {
	// NORMALISE(0, 0) — zero value returns 64 shifts, (0, 0)
	occam := `PROC main()
  INT places, nhi, nlo:
  SEQ
    places, nhi, nlo := NORMALISE(0, 0)
    print.int(places)
    print.int(nhi)
    print.int(nlo)
:
`
	output := transpileCompileRun(t, occam)
	expected := "64\n0\n0\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_SHIFTRIGHT(t *testing.T) {
	// SHIFTRIGHT(hi, lo, n) — shift 64-bit (hi:lo) right by n
	// SHIFTRIGHT(0, 16, 2) = shift 16 right by 2 = (0, 4)
	occam := `PROC main()
  INT rhi, rlo:
  SEQ
    rhi, rlo := SHIFTRIGHT(0, 16, 2)
    print.int(rhi)
    print.int(rlo)
:
`
	output := transpileCompileRun(t, occam)
	expected := "0\n4\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_SHIFTLEFT(t *testing.T) {
	// SHIFTLEFT(hi, lo, n) — shift 64-bit (hi:lo) left by n
	// SHIFTLEFT(0, 1, 4) = shift 1 left by 4 = (0, 16)
	occam := `PROC main()
  INT rhi, rlo:
  SEQ
    rhi, rlo := SHIFTLEFT(0, 1, 4)
    print.int(rhi)
    print.int(rlo)
:
`
	output := transpileCompileRun(t, occam)
	expected := "0\n16\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_SHIFTLEFTCrossWord(t *testing.T) {
	// Shift a value from lo into hi word
	// SHIFTLEFT(0, 1, 32) = (1, 0) — bit moves from lo to hi
	occam := `PROC main()
  INT rhi, rlo:
  SEQ
    rhi, rlo := SHIFTLEFT(0, 1, 32)
    print.int(rhi)
    print.int(rlo)
:
`
	output := transpileCompileRun(t, occam)
	expected := "1\n0\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}
File created successfully at: /home/david/projects/code-associates/occam2go/codegen/e2e_intrinsics_test.go
📝 Write e2e_retypes_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_retypes_test.go
package codegen

import "testing"

func TestE2E_RetypesFloat32ToInt(t *testing.T) {
	// VAL INT X RETYPES X : where X is a REAL32 parameter
	// Reinterpret float32(1.0) as int → IEEE 754: 0x3F800000 = 1065353216
	occam := `PROC show.bits(VAL REAL32 x)
  VAL INT bits RETYPES x :
  SEQ
    print.int(bits)
:

SEQ
  show.bits(REAL32 1)
`
	output := transpileCompileRun(t, occam)
	expected := "1065353216\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_RetypesFloat32Zero(t *testing.T) {
	// float32(0.0) → bits = 0
	occam := `PROC show.bits(VAL REAL32 x)
  VAL INT bits RETYPES x :
  SEQ
    print.int(bits)
:

SEQ
  show.bits(REAL32 0)
`
	output := transpileCompileRun(t, occam)
	expected := "0\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_RetypesFloat32NegOne(t *testing.T) {
	// float32(-1.0) → IEEE 754: 0xBF800000 = -1082130432 (as signed int32)
	occam := `PROC show.bits(VAL REAL32 x)
  VAL INT bits RETYPES x :
  SEQ
    print.int(bits)
:

SEQ
  REAL32 v:
  v := REAL32 1
  v := REAL32 0 - v
  show.bits(v)
`
	output := transpileCompileRun(t, occam)
	expected := "-1082130432\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_RetypesSameNameShadow(t *testing.T) {
	// The classic pattern: VAL INT X RETYPES X : where param is also named X
	// Tests the RETYPES parameter rename mechanism (_rp_X)
	occam := `PROC bits.of(VAL REAL32 X)
  VAL INT X RETYPES X :
  SEQ
    print.int(X)
:

SEQ
  bits.of(REAL32 2)
`
	// float32(2.0) = 0x40000000 = 1073741824
	output := transpileCompileRun(t, occam)
	expected := "1073741824\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_RetypesFloat64ToIntPair(t *testing.T) {
	// VAL [2]INT X RETYPES X : reinterpret float64 as two int32 words
	// float64(1.0) = 0x3FF0000000000000
	// lo = 0x00000000 = 0, hi = 0x3FF00000 = 1072693248
	occam := `PROC show.bits64(VAL REAL64 X)
  VAL [2]INT X RETYPES X :
  SEQ
    print.int(X[0])
    print.int(X[1])
:

SEQ
  show.bits64(REAL64 1)
`
	output := transpileCompileRun(t, occam)
	expected := "0\n1072693248\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_RetypesFloat64Zero(t *testing.T) {
	// float64(0.0) → both words should be 0
	occam := `PROC show.bits64(VAL REAL64 X)
  VAL [2]INT X RETYPES X :
  SEQ
    print.int(X[0])
    print.int(X[1])
:

SEQ
  show.bits64(REAL64 0)
`
	output := transpileCompileRun(t, occam)
	expected := "0\n0\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}
File created successfully at: /home/david/projects/code-associates/occam2go/codegen/e2e_retypes_test.go

Now let me write the proc parameter variants test and the string/print tests.

📝 Write e2e_params_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_params_test.go
package codegen

import "testing"

func TestE2E_ResultQualifier(t *testing.T) {
	// RESULT INT x is semantically the same as non-VAL (pointer param)
	occam := `PROC compute(VAL INT a, VAL INT b, RESULT INT sum)
  sum := a + b
:

SEQ
  INT s:
  compute(10, 32, s)
  print.int(s)
`
	output := transpileCompileRun(t, occam)
	expected := "42\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_ResultQualifierMultiple(t *testing.T) {
	// Multiple RESULT params
	occam := `PROC divmod(VAL INT a, VAL INT b, RESULT INT quot, RESULT INT rem)
  SEQ
    quot := a / b
    rem := a \ b
:

SEQ
  INT q, r:
  divmod(42, 5, q, r)
  print.int(q)
  print.int(r)
`
	output := transpileCompileRun(t, occam)
	expected := "8\n2\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_FixedSizeArrayParam(t *testing.T) {
	// [2]INT param → pointer to fixed-size array
	occam := `PROC swap([2]INT arr)
  INT tmp:
  SEQ
    tmp := arr[0]
    arr[0] := arr[1]
    arr[1] := tmp
:

SEQ
  [2]INT pair:
  pair[0] := 10
  pair[1] := 20
  swap(pair)
  print.int(pair[0])
  print.int(pair[1])
`
	output := transpileCompileRun(t, occam)
	expected := "20\n10\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_SharedTypeChanParams(t *testing.T) {
	// PROC f(CHAN OF INT a?, b?) — type applies to both a and b
	occam := `PROC relay(CHAN OF INT input?, output!)
  INT x:
  SEQ
    input ? x
    output ! x
:

SEQ
  CHAN OF INT c1:
  CHAN OF INT c2:
  INT result:
  PAR
    c1 ! 42
    relay(c1, c2)
    SEQ
      c2 ? result
      print.int(result)
`
	output := transpileCompileRun(t, occam)
	expected := "42\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_SharedTypeIntParams(t *testing.T) {
	// PROC f(VAL INT a, b) — type applies to both a and b
	occam := `PROC add(VAL INT a, b, INT result)
  result := a + b
:

SEQ
  INT r:
  add(10, 32, r)
  print.int(r)
`
	output := transpileCompileRun(t, occam)
	expected := "42\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_ValOpenArrayByteParam(t *testing.T) {
	// VAL []BYTE param with string literal → wraps with []byte()
	occam := `PROC show.length(VAL []BYTE s)
  print.int(SIZE s)
:

SEQ
  show.length("hello")
`
	output := transpileCompileRun(t, occam)
	expected := "5\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}
File created successfully at: /home/david/projects/code-associates/occam2go/codegen/e2e_params_test.go
📝 Write e2e_strings_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_strings_test.go
package codegen

import "testing"

func TestE2E_ValByteArrayAbbreviation(t *testing.T) {
	// VAL []BYTE s IS "hello": — open array byte abbreviation
	occam := `SEQ
  VAL []BYTE s IS "hello":
  print.int(SIZE s)
`
	output := transpileCompileRun(t, occam)
	expected := "5\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_PrintString(t *testing.T) {
	// print.string should output the string content
	occam := `SEQ
  print.string("hello world")
`
	output := transpileCompileRun(t, occam)
	expected := "hello world\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_PrintNewline(t *testing.T) {
	// print.newline should output a blank line
	occam := `SEQ
  print.int(1)
  print.newline()
  print.int(2)
`
	output := transpileCompileRun(t, occam)
	expected := "1\n\n2\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_PrintStringAndNewline(t *testing.T) {
	// Combined usage of print.string and print.newline
	occam := `SEQ
  print.string("first")
  print.string("second")
`
	output := transpileCompileRun(t, occam)
	expected := "first\nsecond\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_StringWithEscapes(t *testing.T) {
	// Occam escape sequences in string: *n = newline, *t = tab
	occam := `SEQ
  print.string("a*tb")
`
	output := transpileCompileRun(t, occam)
	expected := "a\tb\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}
File created successfully at: /home/david/projects/code-associates/occam2go/codegen/e2e_strings_test.go
📝 Write e2e_misc_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_misc_test.go
package codegen

import (
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	"github.com/codeassociates/occam2go/lexer"
	"github.com/codeassociates/occam2go/parser"
)

func TestE2E_SkipStatement(t *testing.T) {
	// SKIP as a standalone statement — should be a no-op
	occam := `SEQ
  print.int(1)
  SKIP
  print.int(2)
`
	output := transpileCompileRun(t, occam)
	expected := "1\n2\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_SkipInPar(t *testing.T) {
	// SKIP in a PAR branch — one branch does nothing
	occam := `SEQ
  INT x:
  PAR
    SKIP
    x := 42
  print.int(x)
`
	output := transpileCompileRun(t, occam)
	expected := "42\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_StopReached(t *testing.T) {
	// STOP should print an error message to stderr and halt (deadlock via select{})
	// We verify the program exits with non-zero status and prints to stderr
	occamSource := `SEQ
  STOP
`
	l := lexer.New(occamSource)
	p := parser.New(l)
	program := p.ParseProgram()

	if len(p.Errors()) > 0 {
		for _, err := range p.Errors() {
			t.Errorf("parser error: %s", err)
		}
		t.FailNow()
	}

	gen := New()
	goCode := gen.Generate(program)

	tmpDir, err := os.MkdirTemp("", "occam2go-test-*")
	if err != nil {
		t.Fatalf("failed to create temp dir: %v", err)
	}
	defer os.RemoveAll(tmpDir)

	goFile := filepath.Join(tmpDir, "main.go")
	if err := os.WriteFile(goFile, []byte(goCode), 0644); err != nil {
		t.Fatalf("failed to write Go file: %v", err)
	}

	binFile := filepath.Join(tmpDir, "main")
	compileCmd := exec.Command("go", "build", "-o", binFile, goFile)
	compileOutput, err := compileCmd.CombinedOutput()
	if err != nil {
		t.Fatalf("compilation failed: %v\nOutput: %s\nGo code:\n%s", err, compileOutput, goCode)
	}

	// Run with a timeout — STOP causes a deadlock (select{})
	runCmd := exec.Command(binFile)
	err = runCmd.Start()
	if err != nil {
		t.Fatalf("failed to start: %v", err)
	}

	// The program should deadlock, so we just verify it compiles and starts.
	// Kill it after a short delay.
	done := make(chan error, 1)
	go func() {
		done <- runCmd.Wait()
	}()

	select {
	case err := <-done:
		// If it exited, it should be non-zero (fatal error: all goroutines are asleep)
		if err == nil {
			t.Errorf("expected STOP to cause non-zero exit, but exited successfully")
		}
	case <-func() <-chan struct{} {
		ch := make(chan struct{})
		go func() {
			// Wait 2 seconds then signal
			exec.Command("sleep", "0.5").Run()
			close(ch)
		}()
		return ch
	}():
		// Expected: program is stuck in select{}, kill it
		runCmd.Process.Kill()
	}
}

func TestE2E_ModuloOperator(t *testing.T) {
	// \ is the modulo operator in occam, maps to % in Go
	occam := `SEQ
  INT x:
  x := 42 \ 5
  print.int(x)
`
	output := transpileCompileRun(t, occam)
	expected := "2\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_ModuloInExpression(t *testing.T) {
	// Modulo used in a larger expression
	occam := `SEQ
  INT x:
  x := (17 \ 5) + (10 \ 3)
  print.int(x)
`
	output := transpileCompileRun(t, occam)
	// 17 % 5 = 2, 10 % 3 = 1, sum = 3
	expected := "3\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_AltWithBooleanGuard(t *testing.T) {
	// ALT with boolean guard: TRUE & c ? x
	occam := `SEQ
  CHAN OF INT c1:
  CHAN OF INT c2:
  INT result:
  BOOL allow:
  allow := FALSE
  PAR
    SEQ
      c1 ! 10
      c2 ! 42
    ALT
      allow & c1 ? result
        SKIP
      TRUE & c2 ? result
        SKIP
  print.int(result)
`
	output := transpileCompileRun(t, occam)
	expected := "42\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_AltWithTrueGuard(t *testing.T) {
	// ALT where guard evaluates to TRUE for the first channel
	occam := `SEQ
  CHAN OF INT c:
  INT result:
  PAR
    c ! 99
    ALT
      TRUE & c ? result
        print.int(result)
`
	output := transpileCompileRun(t, occam)
	expected := "99\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_MostNegReal32(t *testing.T) {
	// MOSTNEG REAL32 → -math.MaxFloat32 (a very large negative number)
	occam := `SEQ
  REAL32 x:
  x := MOSTNEG REAL32
  IF
    x < (REAL32 0)
      print.int(1)
    TRUE
      print.int(0)
`
	output := transpileCompileRun(t, occam)
	expected := "1\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_MostPosReal32(t *testing.T) {
	// MOSTPOS REAL32 → math.MaxFloat32
	occam := `SEQ
  REAL32 x:
  x := MOSTPOS REAL32
  IF
    x > (REAL32 0)
      print.int(1)
    TRUE
      print.int(0)
`
	output := transpileCompileRun(t, occam)
	expected := "1\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_MostNegReal64(t *testing.T) {
	// MOSTNEG REAL64 → -math.MaxFloat64
	occam := `SEQ
  REAL64 x:
  x := MOSTNEG REAL64
  IF
    x < (REAL64 0)
      print.int(1)
    TRUE
      print.int(0)
`
	output := transpileCompileRun(t, occam)
	expected := "1\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_MostPosReal64(t *testing.T) {
	// MOSTPOS REAL64 → math.MaxFloat64
	occam := `SEQ
  REAL64 x:
  x := MOSTPOS REAL64
  IF
    x > (REAL64 0)
      print.int(1)
    TRUE
      print.int(0)
`
	output := transpileCompileRun(t, occam)
	expected := "1\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_ShorthandSliceFromZero(t *testing.T) {
	// [arr FOR m] — shorthand for [arr FROM 0 FOR m]
	occam := `SEQ
  [5]INT arr:
  SEQ i = 0 FOR 5
    arr[i] := i * 10
  INT sum:
  sum := 0
  VAL first3 IS [arr FOR 3]:
  SEQ i = 0 FOR 3
    sum := sum + first3[i]
  print.int(sum)
`
	output := transpileCompileRun(t, occam)
	// 0 + 10 + 20 = 30
	expected := "30\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_StringToByteSliceWrapping(t *testing.T) {
	// When passing a string literal to a []BYTE param, it should wrap with []byte()
	occam := `PROC first.char(VAL []BYTE s, INT result)
  result := INT s[0]
:

SEQ
  INT ch:
  first.char("hello", ch)
  print.int(ch)
`
	output := transpileCompileRun(t, occam)
	// 'h' = 104
	expected := "104\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_GoReservedWordEscaping(t *testing.T) {
	// Test that occam identifiers matching Go reserved words are escaped
	// e.g., a variable named "string" should work
	occam := `SEQ
  INT len:
  len := 42
  print.int(len)
`
	output := transpileCompileRun(t, occam)
	expected := "42\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_GoReservedWordByte(t *testing.T) {
	// "byte" is a Go reserved word — should be escaped to _byte
	occam := `SEQ
  INT byte:
  byte := 99
  print.int(byte)
`
	output := transpileCompileRun(t, occam)
	expected := "99\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_MultiLineExpression(t *testing.T) {
	// Multi-line expression with continuation operator at end of line
	occam := `SEQ
  INT x:
  x := 10 +
    20 +
    12
  print.int(x)
`
	output := transpileCompileRun(t, occam)
	expected := "42\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_MultiLineParenExpression(t *testing.T) {
	// Expression spanning multiple lines inside parentheses
	occam := `SEQ
  INT x:
  x := (10
    + 20
    + 12)
  print.int(x)
`
	output := transpileCompileRun(t, occam)
	expected := "42\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_NegativeIntLiteral(t *testing.T) {
	// Negative integer literals (unary minus)
	occam := `SEQ
  INT x:
  x := -42
  print.int(x)
`
	output := transpileCompileRun(t, occam)
	expected := "-42\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_NotOperator(t *testing.T) {
	// NOT boolean operator
	occam := `SEQ
  BOOL x:
  x := NOT TRUE
  print.bool(x)
`
	output := transpileCompileRun(t, occam)
	expected := "false\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_LogicalAndOr(t *testing.T) {
	// AND / OR operators
	occam := `SEQ
  BOOL a, b:
  a := TRUE AND FALSE
  b := TRUE OR FALSE
  print.bool(a)
  print.bool(b)
`
	output := transpileCompileRun(t, occam)
	expected := "false\ntrue\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_NestedIfInSeq(t *testing.T) {
	// Nested IF inside SEQ with variable declarations
	occam := `SEQ
  INT x:
  x := 5
  INT y:
  y := 0
  IF
    x > 3
      IF
        x < 10
          y := 1
        TRUE
          y := 2
    TRUE
      y := 3
  print.int(y)
`
	output := transpileCompileRun(t, occam)
	expected := "1\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_WhileWithBreakCondition(t *testing.T) {
	// WHILE loop counting to a target
	occam := `SEQ
  INT sum, i:
  sum := 0
  i := 1
  WHILE i <= 10
    SEQ
      sum := sum + i
      i := i + 1
  print.int(sum)
`
	output := transpileCompileRun(t, occam)
	// 1+2+...+10 = 55
	expected := "55\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_CaseWithMultipleArms(t *testing.T) {
	// CASE with several branches
	occam := `SEQ
  INT x, result:
  x := 3
  CASE x
    1
      result := 10
    2
      result := 20
    3
      result := 30
    ELSE
      result := 0
  print.int(result)
`
	output := transpileCompileRun(t, occam)
	expected := "30\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_EqualNotEqual(t *testing.T) {
	// = and <> operators
	occam := `SEQ
  print.bool(5 = 5)
  print.bool(5 <> 3)
  print.bool(5 = 3)
  print.bool(5 <> 5)
`
	output := transpileCompileRun(t, occam)
	expected := "true\ntrue\nfalse\nfalse\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_CompileOnly_StopInProc(t *testing.T) {
	// STOP inside a proc — just verify it compiles (don't run, it would deadlock)
	occamSource := `PROC fatal()
  STOP
:

SEQ
  print.int(42)
`
	l := lexer.New(occamSource)
	p := parser.New(l)
	program := p.ParseProgram()

	if len(p.Errors()) > 0 {
		for _, err := range p.Errors() {
			t.Errorf("parser error: %s", err)
		}
		t.FailNow()
	}

	gen := New()
	goCode := gen.Generate(program)

	tmpDir, err := os.MkdirTemp("", "occam2go-test-*")
	if err != nil {
		t.Fatalf("failed to create temp dir: %v", err)
	}
	defer os.RemoveAll(tmpDir)

	goFile := filepath.Join(tmpDir, "main.go")
	if err := os.WriteFile(goFile, []byte(goCode), 0644); err != nil {
		t.Fatalf("failed to write Go file: %v", err)
	}

	// Just check it compiles
	compileCmd := exec.Command("go", "vet", goFile)
	compileOutput, err := compileCmd.CombinedOutput()
	if err != nil {
		t.Fatalf("compilation failed: %v\nOutput: %s\nGo code:\n%s", err, compileOutput, goCode)
	}
}

func TestE2E_NestedReplicatedSeq(t *testing.T) {
	// Nested replicated SEQ — matrix-like access
	occam := `SEQ
  INT sum:
  sum := 0
  SEQ i = 0 FOR 3
    SEQ j = 0 FOR 3
      sum := sum + ((i * 3) + j)
  print.int(sum)
`
	output := transpileCompileRun(t, occam)
	// 0+1+2+3+4+5+6+7+8 = 36
	expected := "36\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_ArraySliceAssignment(t *testing.T) {
	// [arr FROM n FOR m] := src — copy slice
	occam := `SEQ
  [5]INT dst:
  [3]INT src:
  SEQ i = 0 FOR 5
    dst[i] := 0
  src[0] := 10
  src[1] := 20
  src[2] := 30
  [dst FROM 1 FOR 3] := src
  SEQ i = 0 FOR 5
    print.int(dst[i])
`
	output := transpileCompileRun(t, occam)
	expected := "0\n10\n20\n30\n0\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_FunctionCallInCondition(t *testing.T) {
	// Function call used as condition in IF
	occam := `BOOL FUNCTION is.positive(VAL INT x)
  IS x > 0

SEQ
  IF
    is.positive(42)
      print.int(1)
    TRUE
      print.int(0)
`
	output := transpileCompileRun(t, occam)
	expected := "1\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_RecursiveFunction(t *testing.T) {
	// Recursive function (factorial)
	occam := `INT FUNCTION factorial(VAL INT n)
  INT result:
  VALOF
    IF
      n <= 1
        result := 1
      TRUE
        result := n * factorial(n - 1)
    RESULT result

SEQ
  print.int(factorial(5))
`
	output := transpileCompileRun(t, occam)
	expected := "120\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_MultiLineProcParams(t *testing.T) {
	// Procedure with parameters spanning multiple lines (paren suppression)
	occam := `PROC add(
  VAL INT a,
  VAL INT b,
  INT result)
  result := a + b
:

SEQ
  INT r:
  add(10, 32, r)
  print.int(r)
`
	output := transpileCompileRun(t, occam)
	expected := "42\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_VetOutputClean(t *testing.T) {
	// Verify go vet passes on generated code for a non-trivial program
	occamSource := `PROC compute(VAL INT n)
  INT x:
  PROC helper()
    x := n * 2
  :
  SEQ
    helper()
    print.int(x)
:

SEQ
  compute(21)
`
	l := lexer.New(occamSource)
	p := parser.New(l)
	program := p.ParseProgram()

	if len(p.Errors()) > 0 {
		for _, err := range p.Errors() {
			t.Errorf("parser error: %s", err)
		}
		t.FailNow()
	}

	gen := New()
	goCode := gen.Generate(program)

	tmpDir, err := os.MkdirTemp("", "occam2go-test-*")
	if err != nil {
		t.Fatalf("failed to create temp dir: %v", err)
	}
	defer os.RemoveAll(tmpDir)

	goFile := filepath.Join(tmpDir, "main.go")
	if err := os.WriteFile(goFile, []byte(goCode), 0644); err != nil {
		t.Fatalf("failed to write Go file: %v", err)
	}

	vetCmd := exec.Command("go", "vet", goFile)
	vetOutput, err := vetCmd.CombinedOutput()
	if err != nil {
		t.Fatalf("go vet failed: %v\nOutput: %s\nGo code:\n%s", err, vetOutput, goCode)
	}

	// Also verify it runs correctly
	output := transpileCompileRun(t, occamSource)
	if strings.TrimSpace(output) != "42" {
		t.Errorf("expected 42, got %q", output)
	}
}
File created successfully at: /home/david/projects/code-associates/occam2go/codegen/e2e_misc_test.go

Now let me write the lexer tests for the gaps.

Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/lexer/lexer_test.go"
}
     1→package lexer
     2→
     3→import (
     4→	"testing"
     5→)
     6→
     7→func TestBasicTokens(t *testing.T) {
     8→	input := `INT x:
     9→x := 5
    10→`
    11→	tests := []struct {
    12→		expectedType    TokenType
    13→		expectedLiteral string
    14→	}{
    15→		{INT_TYPE, "INT"},
    16→		{IDENT, "x"},
    17→		{COLON, ":"},
    18→		{NEWLINE, "\\n"},
    19→		{IDENT, "x"},
    20→		{ASSIGN, ":="},
    21→		{INT, "5"},
    22→		{NEWLINE, "\\n"},
    23→		{EOF, ""},
    24→	}
    25→
    26→	l := New(input)
    27→	for i, tt := range tests {
    28→		tok := l.NextToken()
    29→		if tok.Type != tt.expectedType {
    30→			t.Fatalf("tests[%d] - tokentype wrong. expected=%q, got=%q (literal=%q)",
    31→				i, tt.expectedType, tok.Type, tok.Literal)
    32→		}
    33→		if tok.Literal != tt.expectedLiteral {
    34→			t.Fatalf("tests[%d] - literal wrong. expected=%q, got=%q",
    35→				i, tt.expectedLiteral, tok.Literal)
    36→		}
    37→	}
    38→}
    39→
    40→func TestIndentation(t *testing.T) {
    41→	input := `SEQ
    42→  INT x:
    43→  x := 10
    44→`
    45→	tests := []struct {
    46→		expectedType    TokenType
    47→		expectedLiteral string
    48→	}{
    49→		{SEQ, "SEQ"},
    50→		{NEWLINE, "\\n"},
    51→		{INDENT, ""},
    52→		{INT_TYPE, "INT"},
    53→		{IDENT, "x"},
    54→		{COLON, ":"},
    55→		{NEWLINE, "\\n"},
    56→		{IDENT, "x"},
    57→		{ASSIGN, ":="},
    58→		{INT, "10"},
    59→		{NEWLINE, "\\n"},
    60→		{DEDENT, ""},
    61→		{EOF, ""},
    62→	}
    63→
    64→	l := New(input)
    65→	for i, tt := range tests {
    66→		tok := l.NextToken()
    67→		if tok.Type != tt.expectedType {
    68→			t.Fatalf("tests[%d] - tokentype wrong. expected=%q, got=%q (literal=%q)",
    69→				i, tt.expectedType, tok.Type, tok.Literal)
    70→		}
    71→	}
    72→}
    73→
    74→func TestOperators(t *testing.T) {
    75→	input := `x + y - z * a / b
    76→x < y
    77→x > y
    78→x <= y
    79→x >= y
    80→x = y
    81→x <> y
    82→`
    83→	l := New(input)
    84→
    85→	expected := []TokenType{
    86→		IDENT, PLUS, IDENT, MINUS, IDENT, MULTIPLY, IDENT, DIVIDE, IDENT, NEWLINE,
    87→		IDENT, LT, IDENT, NEWLINE,
    88→		IDENT, GT, IDENT, NEWLINE,
    89→		IDENT, LE, IDENT, NEWLINE,
    90→		IDENT, GE, IDENT, NEWLINE,
    91→		IDENT, EQ, IDENT, NEWLINE,
    92→		IDENT, NEQ, IDENT, NEWLINE,
    93→		EOF,
    94→	}
    95→
    96→	for i, exp := range expected {
    97→		tok := l.NextToken()
    98→		if tok.Type != exp {
    99→			t.Fatalf("tests[%d] - tokentype wrong. expected=%q, got=%q",
   100→				i, exp, tok.Type)
   101→		}
   102→	}
   103→}
   104→
   105→func TestKeywords(t *testing.T) {
   106→	input := "SEQ PAR ALT IF WHILE PROC INT BYTE BOOL TRUE FALSE\n"
   107→	expected := []TokenType{
   108→		SEQ, PAR, ALT, IF, WHILE, PROC, INT_TYPE, BYTE_TYPE, BOOL_TYPE, TRUE, FALSE,
   109→		NEWLINE, EOF,
   110→	}
   111→
   112→	l := New(input)
   113→	for i, exp := range expected {
   114→		tok := l.NextToken()
   115→		if tok.Type != exp {
   116→			t.Fatalf("tests[%d] - tokentype wrong. expected=%q, got=%q (literal=%q)",
   117→				i, exp, tok.Type, tok.Literal)
   118→		}
   119→	}
   120→}
   121→
   122→func TestComments(t *testing.T) {
   123→	input := `INT x: -- this is a comment
   124→x := 5
   125→`
   126→	tests := []struct {
   127→		expectedType    TokenType
   128→		expectedLiteral string
   129→	}{
   130→		{INT_TYPE, "INT"},
   131→		{IDENT, "x"},
   132→		{COLON, ":"},
   133→		{NEWLINE, "\\n"},
   134→		{IDENT, "x"},
   135→		{ASSIGN, ":="},
   136→		{INT, "5"},
   137→		{NEWLINE, "\\n"},
   138→		{EOF, ""},
   139→	}
   140→
   141→	l := New(input)
   142→	for i, tt := range tests {
   143→		tok := l.NextToken()
   144→		if tok.Type != tt.expectedType {
   145→			t.Fatalf("tests[%d] - tokentype wrong. expected=%q, got=%q",
   146→				i, tt.expectedType, tok.Type)
   147→		}
   148→	}
   149→}
   150→
   151→func TestBitwiseOperators(t *testing.T) {
   152→	input := "a /\\ b\n"
   153→	l := New(input)
   154→	expected := []struct {
   155→		typ TokenType
   156→		lit string
   157→	}{
   158→		{IDENT, "a"},
   159→		{BITAND, "/\\"},
   160→		{IDENT, "b"},
   161→		{NEWLINE, "\\n"},
   162→		{EOF, ""},
   163→	}
   164→	for i, exp := range expected {
   165→		tok := l.NextToken()
   166→		if tok.Type != exp.typ {
   167→			t.Fatalf("bitand[%d] - type wrong. expected=%q, got=%q (literal=%q)",
   168→				i, exp.typ, tok.Type, tok.Literal)
   169→		}
   170→		if tok.Literal != exp.lit {
   171→			t.Fatalf("bitand[%d] - literal wrong. expected=%q, got=%q",
   172→				i, exp.lit, tok.Literal)
   173→		}
   174→	}
   175→
   176→	// Test all bitwise operators in sequence
   177→	input2 := "a \\/ b >< c ~ d << e >> f\n"
   178→	l2 := New(input2)
   179→	expected2 := []struct {
   180→		typ TokenType
   181→		lit string
   182→	}{
   183→		{IDENT, "a"},
   184→		{BITOR, "\\/"},
   185→		{IDENT, "b"},
   186→		{BITXOR, "><"},
   187→		{IDENT, "c"},
   188→		{BITNOT, "~"},
   189→		{IDENT, "d"},
   190→		{LSHIFT, "<<"},
   191→		{IDENT, "e"},
   192→		{RSHIFT, ">>"},
   193→		{IDENT, "f"},
   194→		{NEWLINE, "\\n"},
   195→		{EOF, ""},
   196→	}
   197→	for i, exp := range expected2 {
   198→		tok := l2.NextToken()
   199→		if tok.Type != exp.typ {
   200→			t.Fatalf("bitwise[%d] - type wrong. expected=%q, got=%q (literal=%q)",
   201→				i, exp.typ, tok.Type, tok.Literal)
   202→		}
   203→		if tok.Literal != exp.lit {
   204→			t.Fatalf("bitwise[%d] - literal wrong. expected=%q, got=%q",
   205→				i, exp.lit, tok.Literal)
   206→		}
   207→	}
   208→}
   209→
   210→func TestBitwiseVsArithmetic(t *testing.T) {
   211→	// Ensure / alone is still DIVIDE and \ alone is still MODULO
   212→	input := "a / b \\ c\n"
   213→	l := New(input)
   214→	expected := []TokenType{IDENT, DIVIDE, IDENT, MODULO, IDENT, NEWLINE, EOF}
   215→	for i, exp := range expected {
   216→		tok := l.NextToken()
   217→		if tok.Type != exp {
   218→			t.Fatalf("tests[%d] - type wrong. expected=%q, got=%q (literal=%q)",
   219→				i, exp, tok.Type, tok.Literal)
   220→		}
   221→	}
   222→}
   223→
   224→func TestHexLiterals(t *testing.T) {
   225→	input := "x := #FF\n"
   226→	tests := []struct {
   227→		expectedType    TokenType
   228→		expectedLiteral string
   229→	}{
   230→		{IDENT, "x"},
   231→		{ASSIGN, ":="},
   232→		{INT, "0xFF"},
   233→		{NEWLINE, "\\n"},
   234→		{EOF, ""},
   235→	}
   236→
   237→	l := New(input)
   238→	for i, tt := range tests {
   239→		tok := l.NextToken()
   240→		if tok.Type != tt.expectedType {
   241→			t.Fatalf("tests[%d] - tokentype wrong. expected=%q, got=%q (literal=%q)",
   242→				i, tt.expectedType, tok.Type, tok.Literal)
   243→		}
   244→		if tok.Literal != tt.expectedLiteral {
   245→			t.Fatalf("tests[%d] - literal wrong. expected=%q, got=%q",
   246→				i, tt.expectedLiteral, tok.Literal)
   247→		}
   248→	}
   249→
   250→	// Test lowercase hex digits
   251→	input2 := "#1a2b\n"
   252→	l2 := New(input2)
   253→	tok := l2.NextToken()
   254→	if tok.Type != INT {
   255→		t.Fatalf("hex lowercase - type wrong. expected=%q, got=%q", INT, tok.Type)
   256→	}
   257→	if tok.Literal != "0x1a2b" {
   258→		t.Fatalf("hex lowercase - literal wrong. expected=%q, got=%q", "0x1a2b", tok.Literal)
   259→	}
   260→
   261→	// Test #0
   262→	input3 := "#0\n"
   263→	l3 := New(input3)
   264→	tok3 := l3.NextToken()
   265→	if tok3.Type != INT {
   266→		t.Fatalf("hex zero - type wrong. expected=%q, got=%q", INT, tok3.Type)
   267→	}
   268→	if tok3.Literal != "0x0" {
   269→		t.Fatalf("hex zero - literal wrong. expected=%q, got=%q", "0x0", tok3.Literal)
   270→	}
   271→}
   272→
   273→func TestNestedIndentation(t *testing.T) {
   274→	input := `SEQ
   275→  INT x:
   276→  PAR
   277→    x := 1
   278→    x := 2
   279→  x := 3
   280→`
   281→	expected := []TokenType{
   282→		SEQ, NEWLINE,
   283→		INDENT, INT_TYPE, IDENT, COLON, NEWLINE,
   284→		PAR, NEWLINE,
   285→		INDENT, IDENT, ASSIGN, INT, NEWLINE,
   286→		IDENT, ASSIGN, INT, NEWLINE,
   287→		DEDENT, IDENT, ASSIGN, INT, NEWLINE,
   288→		DEDENT, EOF,
   289→	}
   290→
   291→	l := New(input)
   292→	for i, exp := range expected {
   293→		tok := l.NextToken()
   294→		if tok.Type != exp {
   295→			t.Fatalf("tests[%d] - tokentype wrong. expected=%q, got=%q (literal=%q)",
   296→				i, exp, tok.Type, tok.Literal)
   297→		}
   298→	}
   299→}
   300→

Now let me check what token types exist that I need to reference in the lexer tests.

Grep
{
  "pattern": "^\\t[A-Z_]+ +TokenType = \"",
  "path": "/home/david/projects/code-associates/occam2go/lexer/token.go",
  "output_mode": "content",
  "-n": true
}
No matches found
Grep
{
  "pattern": "TokenType = \\\"",
  "path": "/home/david/projects/code-associates/occam2go/lexer/token.go",
  "output_mode": "content",
  "-n": true
}
No matches found
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/lexer/token.go"
}
     1→package lexer
     2→
     3→type TokenType int
     4→
     5→const (
     6→	// Special tokens
     7→	ILLEGAL TokenType = iota
     8→	EOF
     9→	NEWLINE
    10→	INDENT
    11→	DEDENT
    12→
    13→	// Literals
    14→	IDENT     // variable names, procedure names
    15→	INT       // integer literal
    16→	STRING    // string literal
    17→	BYTE_LIT  // byte literal: 'A', '*n', etc.
    18→
    19→	// Operators
    20→	ASSIGN   // :=
    21→	PLUS     // +
    22→	MINUS    // -
    23→	MULTIPLY // *
    24→	DIVIDE   // /
    25→	MODULO   // \ (backslash in Occam)
    26→	EQ       // =
    27→	NEQ      // <>
    28→	LT       // <
    29→	GT       // >
    30→	LE       // <=
    31→	GE       // >=
    32→	SEND      // !
    33→	RECEIVE   // ?
    34→	AMPERSAND // & (guard separator in ALT)
    35→	BITAND    // /\  (bitwise AND)
    36→	BITOR     // \/  (bitwise OR)
    37→	BITXOR    // ><  (bitwise XOR)
    38→	BITNOT    // ~   (bitwise NOT)
    39→	LSHIFT    // <<  (left shift)
    40→	RSHIFT    // >>  (right shift)
    41→
    42→	// Delimiters
    43→	LPAREN    // (
    44→	RPAREN    // )
    45→	LBRACKET  // [
    46→	RBRACKET  // ]
    47→	COMMA     // ,
    48→	COLON     // :
    49→	SEMICOLON // ;
    50→
    51→	// Keywords
    52→	keyword_beg
    53→	SEQ
    54→	PAR
    55→	ALT
    56→	IF
    57→	CASE
    58→	ELSE
    59→	WHILE
    60→	FOR
    61→	FROM
    62→	PROC
    63→	FUNC
    64→	FUNCTION
    65→	VALOF
    66→	RESULT
    67→	IS
    68→	CHAN
    69→	OF
    70→	TRUE
    71→	FALSE
    72→	NOT
    73→	AND
    74→	OR
    75→	SKIP
    76→	STOP
    77→	INT_TYPE
    78→	BYTE_TYPE
    79→	BOOL_TYPE
    80→	REAL_TYPE
    81→	REAL32_TYPE
    82→	REAL64_TYPE
    83→	TIMER
    84→	AFTER
    85→	VAL
    86→	PROTOCOL
    87→	RECORD
    88→	SIZE_KW
    89→	STEP
    90→	MOSTNEG_KW
    91→	MOSTPOS_KW
    92→	INITIAL
    93→	RETYPES  // RETYPES (bit-level type reinterpretation)
    94→	PLUS_KW  // PLUS (modular addition keyword, distinct from + symbol)
    95→	MINUS_KW // MINUS (modular subtraction keyword, distinct from - symbol)
    96→	TIMES    // TIMES (modular multiplication keyword)
    97→	keyword_end
    98→)
    99→
   100→var tokenNames = map[TokenType]string{
   101→	ILLEGAL: "ILLEGAL",
   102→	EOF:     "EOF",
   103→	NEWLINE: "NEWLINE",
   104→	INDENT:  "INDENT",
   105→	DEDENT:  "DEDENT",
   106→
   107→	IDENT:    "IDENT",
   108→	INT:      "INT",
   109→	STRING:   "STRING",
   110→	BYTE_LIT: "BYTE_LIT",
   111→
   112→	ASSIGN:   ":=",
   113→	PLUS:     "+",
   114→	MINUS:    "-",
   115→	MULTIPLY: "*",
   116→	DIVIDE:   "/",
   117→	MODULO:   "\\",
   118→	EQ:       "=",
   119→	NEQ:      "<>",
   120→	LT:       "<",
   121→	GT:       ">",
   122→	LE:       "<=",
   123→	GE:       ">=",
   124→	SEND:      "!",
   125→	RECEIVE:   "?",
   126→	AMPERSAND: "&",
   127→	BITAND:    "/\\",
   128→	BITOR:     "\\/",
   129→	BITXOR:    "><",
   130→	BITNOT:    "~",
   131→	LSHIFT:    "<<",
   132→	RSHIFT:    ">>",
   133→
   134→	LPAREN:    "(",
   135→	RPAREN:    ")",
   136→	LBRACKET:  "[",
   137→	RBRACKET:  "]",
   138→	COMMA:     ",",
   139→	COLON:     ":",
   140→	SEMICOLON: ";",
   141→
   142→	SEQ:       "SEQ",
   143→	PAR:       "PAR",
   144→	ALT:       "ALT",
   145→	IF:        "IF",
   146→	CASE:      "CASE",
   147→	ELSE:      "ELSE",
   148→	WHILE:     "WHILE",
   149→	FOR:       "FOR",
   150→	FROM:      "FROM",
   151→	PROC:      "PROC",
   152→	FUNC:      "FUNC",
   153→	FUNCTION:  "FUNCTION",
   154→	VALOF:     "VALOF",
   155→	RESULT:    "RESULT",
   156→	IS:        "IS",
   157→	CHAN:      "CHAN",
   158→	OF:        "OF",
   159→	TRUE:      "TRUE",
   160→	FALSE:     "FALSE",
   161→	NOT:       "NOT",
   162→	AND:       "AND",
   163→	OR:        "OR",
   164→	SKIP:      "SKIP",
   165→	STOP:      "STOP",
   166→	INT_TYPE:  "INT",
   167→	BYTE_TYPE: "BYTE",
   168→	BOOL_TYPE: "BOOL",
   169→	REAL_TYPE:   "REAL",
   170→	REAL32_TYPE: "REAL32",
   171→	REAL64_TYPE: "REAL64",
   172→	TIMER:       "TIMER",
   173→	AFTER:    "AFTER",
   174→	VAL:       "VAL",
   175→	PROTOCOL:  "PROTOCOL",
   176→	RECORD:    "RECORD",
   177→	SIZE_KW:    "SIZE",
   178→	STEP:       "STEP",
   179→	MOSTNEG_KW: "MOSTNEG",
   180→	MOSTPOS_KW: "MOSTPOS",
   181→	INITIAL:    "INITIAL",
   182→	RETYPES:    "RETYPES",
   183→	PLUS_KW:    "PLUS",
   184→	MINUS_KW:   "MINUS",
   185→	TIMES:      "TIMES",
   186→}
   187→
   188→var keywords = map[string]TokenType{
   189→	"SEQ":   SEQ,
   190→	"PAR":   PAR,
   191→	"ALT":   ALT,
   192→	"IF":    IF,
   193→	"CASE":  CASE,
   194→	"ELSE":  ELSE,
   195→	"WHILE": WHILE,
   196→	"FOR":   FOR,
   197→	"FROM":  FROM,
   198→	"PROC":  PROC,
   199→	"FUNC":     FUNC,
   200→	"FUNCTION": FUNCTION,
   201→	"VALOF":    VALOF,
   202→	"RESULT":   RESULT,
   203→	"IS":       IS,
   204→	"CHAN":     CHAN,
   205→	"OF":    OF,
   206→	"TRUE":  TRUE,
   207→	"FALSE": FALSE,
   208→	"NOT":   NOT,
   209→	"AND":   AND,
   210→	"OR":    OR,
   211→	"SKIP":  SKIP,
   212→	"STOP":  STOP,
   213→	"INT":   INT_TYPE,
   214→	"BYTE":  BYTE_TYPE,
   215→	"BOOL":  BOOL_TYPE,
   216→	"REAL":   REAL_TYPE,
   217→	"REAL32": REAL32_TYPE,
   218→	"REAL64": REAL64_TYPE,
   219→	"TIMER":  TIMER,
   220→	"AFTER": AFTER,
   221→	"VAL":      VAL,
   222→	"PROTOCOL": PROTOCOL,
   223→	"RECORD":   RECORD,
   224→	"SIZE":     SIZE_KW,
   225→	"STEP":     STEP,
   226→	"MOSTNEG":  MOSTNEG_KW,
   227→	"MOSTPOS":  MOSTPOS_KW,
   228→	"INITIAL":  INITIAL,
   229→	"RETYPES":  RETYPES,
   230→	"PLUS":     PLUS_KW,
   231→	"MINUS":    MINUS_KW,
   232→	"TIMES":    TIMES,
   233→}
   234→
   235→func (t TokenType) String() string {
   236→	if name, ok := tokenNames[t]; ok {
   237→		return name
   238→	}
   239→	return "UNKNOWN"
   240→}
   241→
   242→func LookupIdent(ident string) TokenType {
   243→	if tok, ok := keywords[ident]; ok {
   244→		return tok
   245→	}
   246→	return IDENT
   247→}
   248→
   249→type Token struct {
   250→	Type    TokenType
   251→	Literal string
   252→	Line    int
   253→	Column  int
   254→}
   255→

Now let me write the lexer tests. Let me also check the lexer for paren depth and continuation logic to understand how they work.

Grep
{
  "pattern": "parenDepth|isContinuationOp",
  "path": "/home/david/projects/code-associates/occam2go/lexer/lexer.go",
  "output_mode": "content",
  "-n": true,
  "context": 2
}
19-
20-	/ Parenthesis/bracket depth: suppress INDENT/DEDENT/NEWLINE inside (...) and [...]
21:	parenDepth int
22-
23-	// Last real token type for continuation detection.
--
80-		currentIndent := l.indentStack[len(l.indentStack)-1]
81-
82:		if l.parenDepth > 0 || isContinuationOp(l.lastTokenType) {
83-			/ Inside parentheses/brackets or after a continuation operator:
84-			// suppress INDENT/DEDENT tokens
--
109-	switch l.ch {
110-	case '(':
111:		l.parenDepth++
112-		tok = l.newToken(LPAREN, l.ch)
113-	case ')':
114:		if l.parenDepth > 0 {
115:			l.parenDepth--
116-		}
117-		tok = l.newToken(RPAREN, l.ch)
118-	case '[':
119:		l.parenDepth++
120-		tok = l.newToken(LBRACKET, l.ch)
121-	case ']':
122:		if l.parenDepth > 0 {
123:			l.parenDepth--
124-		}
125-		tok = l.newToken(RBRACKET, l.ch)
--
237-			}
238-		}
239:		if l.parenDepth > 0 || isContinuationOp(l.lastTokenType) {
240-			/ Inside parentheses/brackets or after a continuation operator:
241-			// suppress NEWLINE, get next real token
--
413-}
414-
415:// isContinuationOp returns true if the given token type, when appearing at the
416-// end of a line, indicates that the expression continues on the next line.
417-// This causes NEWLINE and INDENT/DEDENT suppression on the continuation line.
418:func isContinuationOp(t TokenType) bool {
419-	switch t {
420-	case AND, OR,
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/lexer/lexer.go",
  "offset": 415,
  "limit": 20
}
   415→// isContinuationOp returns true if the given token type, when appearing at the
   416→// end of a line, indicates that the expression continues on the next line.
   417→// This causes NEWLINE and INDENT/DEDENT suppression on the continuation line.
   418→func isContinuationOp(t TokenType) bool {
   419→	switch t {
   420→	case AND, OR,
   421→		PLUS, MINUS, MULTIPLY, DIVIDE, MODULO,
   422→		PLUS_KW, MINUS_KW, TIMES,
   423→		EQ, NEQ, LT, GT, LE, GE,
   424→		BITAND, BITOR, BITXOR, LSHIFT, RSHIFT,
   425→		ASSIGN, AFTER:
   426→		return true
   427→	}
   428→	return false
   429→}
   430→
   431→// Tokenize returns all tokens from the input
   432→func Tokenize(input string) []Token {
   433→	// Ensure input ends with newline for consistent processing
   434→	if !strings.HasSuffix(input, "\n") {

Now I have everything I need for the lexer tests.

📝 Write lexer_test2_test.go
/home/david/projects/code-associates/occam2go/lexer/lexer_test2_test.go
package lexer

import "testing"

func TestAllKeywords(t *testing.T) {
	// Test all keywords that aren't covered in TestKeywords
	input := "CASE ELSE FUNC FUNCTION VALOF RESULT IS CHAN OF SKIP STOP VAL PROTOCOL RECORD SIZE STEP MOSTNEG MOSTPOS INITIAL RETYPES PLUS MINUS TIMES TIMER AFTER FOR FROM REAL REAL32 REAL64 NOT AND OR\n"
	expected := []TokenType{
		CASE, ELSE, FUNC, FUNCTION, VALOF, RESULT, IS, CHAN, OF,
		SKIP, STOP, VAL, PROTOCOL, RECORD, SIZE_KW, STEP,
		MOSTNEG_KW, MOSTPOS_KW, INITIAL, RETYPES,
		PLUS_KW, MINUS_KW, TIMES, TIMER, AFTER, FOR, FROM,
		REAL_TYPE, REAL32_TYPE, REAL64_TYPE,
		NOT, AND, OR,
		NEWLINE, EOF,
	}

	l := New(input)
	for i, exp := range expected {
		tok := l.NextToken()
		if tok.Type != exp {
			t.Fatalf("tests[%d] - expected=%q, got=%q (literal=%q)",
				i, exp, tok.Type, tok.Literal)
		}
	}
}

func TestParenDepthSuppressesIndent(t *testing.T) {
	// Inside parentheses, INDENT/DEDENT/NEWLINE should be suppressed
	input := `x := (1
    + 2
    + 3)
`
	expected := []TokenType{
		IDENT,    // x
		ASSIGN,   // :=
		LPAREN,   // (
		INT,      // 1
		PLUS,     // +
		INT,      // 2
		PLUS,     // +
		INT,      // 3
		RPAREN,   // )
		NEWLINE,  // after closing paren
		EOF,
	}

	l := New(input)
	for i, exp := range expected {
		tok := l.NextToken()
		if tok.Type != exp {
			t.Fatalf("paren[%d] - expected=%q, got=%q (literal=%q)",
				i, exp, tok.Type, tok.Literal)
		}
	}
}

func TestBracketDepthSuppressesIndent(t *testing.T) {
	// Inside brackets, INDENT/DEDENT/NEWLINE should be suppressed
	input := `x := [1,
    2,
    3]
`
	expected := []TokenType{
		IDENT,    // x
		ASSIGN,   // :=
		LBRACKET, // [
		INT,      // 1
		COMMA,    // ,
		INT,      // 2
		COMMA,    // ,
		INT,      // 3
		RBRACKET, // ]
		NEWLINE,  // after closing bracket
		EOF,
	}

	l := New(input)
	for i, exp := range expected {
		tok := l.NextToken()
		if tok.Type != exp {
			t.Fatalf("bracket[%d] - expected=%q, got=%q (literal=%q)",
				i, exp, tok.Type, tok.Literal)
		}
	}
}

func TestContinuationOperator(t *testing.T) {
	// Operator at end of line causes NEWLINE/INDENT/DEDENT suppression on next line
	input := `x := a +
    b
`
	expected := []TokenType{
		IDENT,   // x
		ASSIGN,  // :=
		IDENT,   // a
		PLUS,    // +
		IDENT,   // b
		NEWLINE, // after b
		EOF,
	}

	l := New(input)
	for i, exp := range expected {
		tok := l.NextToken()
		if tok.Type != exp {
			t.Fatalf("continuation[%d] - expected=%q, got=%q (literal=%q)",
				i, exp, tok.Type, tok.Literal)
		}
	}
}

func TestContinuationAND(t *testing.T) {
	// AND at end of line should continue
	input := `(x > 0) AND
    (x < 10)
`
	expected := []TokenType{
		LPAREN, IDENT, GT, INT, RPAREN, // (x > 0)
		AND,                             // AND
		LPAREN, IDENT, LT, INT, RPAREN, // (x < 10)
		NEWLINE,
		EOF,
	}

	l := New(input)
	for i, exp := range expected {
		tok := l.NextToken()
		if tok.Type != exp {
			t.Fatalf("cont_and[%d] - expected=%q, got=%q (literal=%q)",
				i, exp, tok.Type, tok.Literal)
		}
	}
}

func TestStringLiteral(t *testing.T) {
	input := `"hello world"` + "\n"
	l := New(input)
	tok := l.NextToken()
	if tok.Type != STRING {
		t.Fatalf("expected STRING, got %q", tok.Type)
	}
	if tok.Literal != "hello world" {
		t.Fatalf("expected literal %q, got %q", "hello world", tok.Literal)
	}
}

func TestStringEscapeSequences(t *testing.T) {
	// Occam escape: *n → \n, *t → \t, *c → \r, ** → *, *" → "
	input := `"a*nb"` + "\n"
	l := New(input)
	tok := l.NextToken()
	if tok.Type != STRING {
		t.Fatalf("expected STRING, got %q", tok.Type)
	}
	if tok.Literal != "a\\nb" {
		t.Fatalf("expected literal %q, got %q", "a\\nb", tok.Literal)
	}
}

func TestByteLiteralToken(t *testing.T) {
	input := "'A'\n"
	l := New(input)
	tok := l.NextToken()
	if tok.Type != BYTE_LIT {
		t.Fatalf("expected BYTE_LIT, got %q (literal=%q)", tok.Type, tok.Literal)
	}
	if tok.Literal != "A" {
		t.Fatalf("expected literal %q, got %q", "A", tok.Literal)
	}
}

func TestByteLiteralEscapeToken(t *testing.T) {
	input := "'*n'\n"
	l := New(input)
	tok := l.NextToken()
	if tok.Type != BYTE_LIT {
		t.Fatalf("expected BYTE_LIT, got %q (literal=%q)", tok.Type, tok.Literal)
	}
	if tok.Literal != "\\n" {
		t.Fatalf("expected literal %q, got %q", "\\n", tok.Literal)
	}
}

func TestSendReceiveTokens(t *testing.T) {
	input := "c ! 42\nc ? x\n"
	expected := []struct {
		typ TokenType
		lit string
	}{
		{IDENT, "c"},
		{SEND, "!"},
		{INT, "42"},
		{NEWLINE, "\\n"},
		{IDENT, "c"},
		{RECEIVE, "?"},
		{IDENT, "x"},
		{NEWLINE, "\\n"},
		{EOF, ""},
	}

	l := New(input)
	for i, exp := range expected {
		tok := l.NextToken()
		if tok.Type != exp.typ {
			t.Fatalf("send_recv[%d] - expected type=%q, got=%q (literal=%q)",
				i, exp.typ, tok.Type, tok.Literal)
		}
	}
}

func TestAmpersandToken(t *testing.T) {
	// & used as guard separator in ALT
	input := "TRUE & c ? x\n"
	expected := []TokenType{TRUE, AMPERSAND, IDENT, RECEIVE, IDENT, NEWLINE, EOF}

	l := New(input)
	for i, exp := range expected {
		tok := l.NextToken()
		if tok.Type != exp {
			t.Fatalf("ampersand[%d] - expected=%q, got=%q", i, exp, tok.Type)
		}
	}
}

func TestSemicolonToken(t *testing.T) {
	input := "c ! 10 ; 20\n"
	expected := []TokenType{IDENT, SEND, INT, SEMICOLON, INT, NEWLINE, EOF}

	l := New(input)
	for i, exp := range expected {
		tok := l.NextToken()
		if tok.Type != exp {
			t.Fatalf("semicolon[%d] - expected=%q, got=%q", i, exp, tok.Type)
		}
	}
}

func TestNestedParenDepth(t *testing.T) {
	// Nested parens: depth tracks correctly
	input := `x := ((1
    + 2)
    + 3)
`
	expected := []TokenType{
		IDENT, ASSIGN,
		LPAREN, LPAREN, INT,
		PLUS, INT, RPAREN,
		PLUS, INT, RPAREN,
		NEWLINE, EOF,
	}

	l := New(input)
	for i, exp := range expected {
		tok := l.NextToken()
		if tok.Type != exp {
			t.Fatalf("nested_paren[%d] - expected=%q, got=%q (literal=%q)",
				i, exp, tok.Type, tok.Literal)
		}
	}
}

func TestMixedParenBracketDepth(t *testing.T) {
	// Mix of parens and brackets, both should suppress indent
	input := `x := arr[(1
    + 2)]
`
	expected := []TokenType{
		IDENT, ASSIGN,
		IDENT, LBRACKET, LPAREN, INT,
		PLUS, INT, RPAREN, RBRACKET,
		NEWLINE, EOF,
	}

	l := New(input)
	for i, exp := range expected {
		tok := l.NextToken()
		if tok.Type != exp {
			t.Fatalf("mixed[%d] - expected=%q, got=%q (literal=%q)",
				i, exp, tok.Type, tok.Literal)
		}
	}
}

func TestLineAndColumnTracking(t *testing.T) {
	input := "INT x:\nx := 5\n"
	l := New(input)

	// INT at line 1, col 1
	tok := l.NextToken()
	if tok.Line != 1 || tok.Column != 1 {
		t.Errorf("INT: expected line=1 col=1, got line=%d col=%d", tok.Line, tok.Column)
	}

	// x at line 1, col 5
	tok = l.NextToken()
	if tok.Line != 1 || tok.Column != 5 {
		t.Errorf("x: expected line=1 col=5, got line=%d col=%d", tok.Line, tok.Column)
	}
}
File created successfully at: /home/david/projects/code-associates/occam2go/lexer/lexer_test2_test.go

Now let me run the tests to see what passes and what needs fixing.

TaskUpdate
{
  "taskId": "1",
  "status": "completed"
}
Updated task #1 status
TaskUpdate
{
  "taskId": "2",
  "status": "completed"
}
Updated task #2 status
TaskUpdate
{
  "taskId": "3",
  "status": "completed"
}
Updated task #3 status
TaskUpdate
{
  "taskId": "4",
  "status": "completed"
}
Updated task #4 status
TaskUpdate
{
  "taskId": "5",
  "status": "completed"
}
Updated task #5 status
TaskUpdate
{
  "taskId": "6",
  "status": "completed"
}
Updated task #6 status
TaskUpdate
{
  "taskId": "7",
  "status": "completed"
}
Updated task #7 status
TaskUpdate
{
  "taskId": "8",
  "status": "in_progress"
}
Updated task #8 status
$ Bash
Run lexer tests
go test ./lexer/ -v -count=1 2>&1 | tail -60
=== RUN   TestBasicTokens
--- PASS: TestBasicTokens (0.00s)
=== RUN   TestIndentation
--- PASS: TestIndentation (0.00s)
=== RUN   TestOperators
--- PASS: TestOperators (0.00s)
=== RUN   TestKeywords
--- PASS: TestKeywords (0.00s)
=== RUN   TestComments
--- PASS: TestComments (0.00s)
=== RUN   TestBitwiseOperators
--- PASS: TestBitwiseOperators (0.00s)
=== RUN   TestBitwiseVsArithmetic
--- PASS: TestBitwiseVsArithmetic (0.00s)
=== RUN   TestHexLiterals
--- PASS: TestHexLiterals (0.00s)
=== RUN   TestNestedIndentation
--- PASS: TestNestedIndentation (0.00s)
=== RUN   TestAllKeywords
    lexer_test2_test.go:22: tests[33] - expected="NEWLINE", got="EOF" (literal="")
--- FAIL: TestAllKeywords (0.00s)
=== RUN   TestParenDepthSuppressesIndent
--- PASS: TestParenDepthSuppressesIndent (0.00s)
=== RUN   TestBracketDepthSuppressesIndent
--- PASS: TestBracketDepthSuppressesIndent (0.00s)
=== RUN   TestContinuationOperator
--- PASS: TestContinuationOperator (0.00s)
=== RUN   TestContinuationAND
--- PASS: TestContinuationAND (0.00s)
=== RUN   TestStringLiteral
--- PASS: TestStringLiteral (0.00s)
=== RUN   TestStringEscapeSequences
    lexer_test2_test.go:157: expected literal "a\\nb", got "a*nb"
--- FAIL: TestStringEscapeSequences (0.00s)
=== RUN   TestByteLiteralToken
--- PASS: TestByteLiteralToken (0.00s)
=== RUN   TestByteLiteralEscapeToken
    lexer_test2_test.go:181: expected literal "\\n", got "*n"
--- FAIL: TestByteLiteralEscapeToken (0.00s)
=== RUN   TestSendReceiveTokens
--- PASS: TestSendReceiveTokens (0.00s)
=== RUN   TestAmpersandToken
--- PASS: TestAmpersandToken (0.00s)
=== RUN   TestSemicolonToken
--- PASS: TestSemicolonToken (0.00s)
=== RUN   TestNestedParenDepth
--- PASS: TestNestedParenDepth (0.00s)
=== RUN   TestMixedParenBracketDepth
--- PASS: TestMixedParenBracketDepth (0.00s)
=== RUN   TestLineAndColumnTracking
--- PASS: TestLineAndColumnTracking (0.00s)
FAIL
FAIL	github.com/codeassociates/occam2go/lexer	0.002s
FAIL

Three failures to fix:

  1. TestAllKeywords - the input doesn't end with \n properly (the NEWLINE is consumed as part of the continuation of OR). Let me check.

Actually, the OR at the end is a continuation operator, so the newline after OR gets suppressed. Let me reorder to put OR not at the end.

  1. TestStringEscapeSequences - escape processing happens in the parser, not the lexer. The lexer preserves the raw *n.

  2. TestByteLiteralEscapeToken - same issue, escape processing is in the parser.

Let me fix these.

✏️ Edit lexer_test2_test.go
/home/david/projects/code-associates/occam2go/lexer/lexer_test2_test.go
	input := "CASE ELSE FUNC FUNCTION VALOF RESULT IS CHAN OF SKIP STOP VAL PROTOCOL RECORD SIZE STEP MOSTNEG MOSTPOS INITIAL RETYPES PLUS MINUS TIMES TIMER AFTER FOR FROM REAL REAL32 REAL64 NOT AND OR\n"
	expected := []TokenType{
		CASE, ELSE, FUNC, FUNCTION, VALOF, RESULT, IS, CHAN, OF,
		SKIP, STOP, VAL, PROTOCOL, RECORD, SIZE_KW, STEP,
		MOSTNEG_KW, MOSTPOS_KW, INITIAL, RETYPES,
		PLUS_KW, MINUS_KW, TIMES, TIMER, AFTER, FOR, FROM,
		REAL_TYPE, REAL32_TYPE, REAL64_TYPE,
		NOT, AND, OR,
		NEWLINE, EOF,
	}
+
	// Note: AND and OR are continuation operators, so they can't be at line end
	// (the lexer would suppress the following NEWLINE). Put them mid-line.
	input := "CASE ELSE FUNC FUNCTION VALOF RESULT IS CHAN OF SKIP STOP VAL PROTOCOL RECORD SIZE STEP MOSTNEG MOSTPOS INITIAL RETYPES PLUS MINUS TIMES TIMER AFTER FOR FROM REAL REAL32 REAL64 NOT AND OR WHILE\n"
	expected := []TokenType{
		CASE, ELSE, FUNC, FUNCTION, VALOF, RESULT, IS, CHAN, OF,
		SKIP, STOP, VAL, PROTOCOL, RECORD, SIZE_KW, STEP,
		MOSTNEG_KW, MOSTPOS_KW, INITIAL, RETYPES,
		PLUS_KW, MINUS_KW, TIMES, TIMER, AFTER, FOR, FROM,
		REAL_TYPE, REAL32_TYPE, REAL64_TYPE,
		NOT, AND, OR, WHILE,
		NEWLINE, EOF,
	}
The file /home/david/projects/code-associates/occam2go/lexer/lexer_test2_test.go has been updated successfully.

Now fix the string escape test — the lexer doesn't process escapes, it preserves them raw.

✏️ Edit lexer_test2_test.go
/home/david/projects/code-associates/occam2go/lexer/lexer_test2_test.go
func TestStringEscapeSequences(t *testing.T) {
	// Occam escape: *n → \n, *t → \t, *c → \r, ** → *, *" → "
	input := `"a*nb"` + "\n"
	l := New(input)
	tok := l.NextToken()
	if tok.Type != STRING {
		t.Fatalf("expected STRING, got %q", tok.Type)
	}
	if tok.Literal != "a\\nb" {
		t.Fatalf("expected literal %q, got %q", "a\\nb", tok.Literal)
	}
}
+
func TestStringEscapeSequences(t *testing.T) {
	// The lexer preserves raw occam escapes (*n, *t, etc.) in string literals.
	// Escape conversion (*n → \n) happens in the parser, not the lexer.
	input := `"a*nb"` + "\n"
	l := New(input)
	tok := l.NextToken()
	if tok.Type != STRING {
		t.Fatalf("expected STRING, got %q", tok.Type)
	}
	if tok.Literal != "a*nb" {
		t.Fatalf("expected literal %q, got %q", "a*nb", tok.Literal)
	}
}
The file /home/david/projects/code-associates/occam2go/lexer/lexer_test2_test.go has been updated successfully.
✏️ Edit lexer_test2_test.go
/home/david/projects/code-associates/occam2go/lexer/lexer_test2_test.go
func TestByteLiteralEscapeToken(t *testing.T) {
	input := "'*n'\n"
	l := New(input)
	tok := l.NextToken()
	if tok.Type != BYTE_LIT {
		t.Fatalf("expected BYTE_LIT, got %q (literal=%q)", tok.Type, tok.Literal)
	}
	if tok.Literal != "\\n" {
		t.Fatalf("expected literal %q, got %q", "\\n", tok.Literal)
	}
}
+
func TestByteLiteralEscapeToken(t *testing.T) {
	// The lexer preserves raw occam escape (*n) in byte literals.
	// Escape conversion happens in the parser.
	input := "'*n'\n"
	l := New(input)
	tok := l.NextToken()
	if tok.Type != BYTE_LIT {
		t.Fatalf("expected BYTE_LIT, got %q (literal=%q)", tok.Type, tok.Literal)
	}
	if tok.Literal != "*n" {
		t.Fatalf("expected literal %q, got %q", "*n", tok.Literal)
	}
}
The file /home/david/projects/code-associates/occam2go/lexer/lexer_test2_test.go has been updated successfully.
$ Bash
Run lexer tests after fixes
go test ./lexer/ -v -count=1 2>&1 | tail -40
=== RUN   TestBitwiseOperators
--- PASS: TestBitwiseOperators (0.00s)
=== RUN   TestBitwiseVsArithmetic
--- PASS: TestBitwiseVsArithmetic (0.00s)
=== RUN   TestHexLiterals
--- PASS: TestHexLiterals (0.00s)
=== RUN   TestNestedIndentation
--- PASS: TestNestedIndentation (0.00s)
=== RUN   TestAllKeywords
--- PASS: TestAllKeywords (0.00s)
=== RUN   TestParenDepthSuppressesIndent
--- PASS: TestParenDepthSuppressesIndent (0.00s)
=== RUN   TestBracketDepthSuppressesIndent
--- PASS: TestBracketDepthSuppressesIndent (0.00s)
=== RUN   TestContinuationOperator
--- PASS: TestContinuationOperator (0.00s)
=== RUN   TestContinuationAND
--- PASS: TestContinuationAND (0.00s)
=== RUN   TestStringLiteral
--- PASS: TestStringLiteral (0.00s)
=== RUN   TestStringEscapeSequences
--- PASS: TestStringEscapeSequences (0.00s)
=== RUN   TestByteLiteralToken
--- PASS: TestByteLiteralToken (0.00s)
=== RUN   TestByteLiteralEscapeToken
--- PASS: TestByteLiteralEscapeToken (0.00s)
=== RUN   TestSendReceiveTokens
--- PASS: TestSendReceiveTokens (0.00s)
=== RUN   TestAmpersandToken
--- PASS: TestAmpersandToken (0.00s)
=== RUN   TestSemicolonToken
--- PASS: TestSemicolonToken (0.00s)
=== RUN   TestNestedParenDepth
--- PASS: TestNestedParenDepth (0.00s)
=== RUN   TestMixedParenBracketDepth
--- PASS: TestMixedParenBracketDepth (0.00s)
=== RUN   TestLineAndColumnTracking
--- PASS: TestLineAndColumnTracking (0.00s)
PASS
ok  	github.com/codeassociates/occam2go/lexer	0.001s

All lexer tests pass. Now let me run the codegen e2e tests. Let me run them in parallel batches.

$ Bash
Run intrinsics e2e tests
go test ./codegen/ -run "TestE2E_LONG|TestE2E_NORMALISE|TestE2E_SHIFT" -v -count=1 2>&1 | tail -60
=== RUN   TestE2E_LONGPROD
    e2e_intrinsics_test.go:21: expected "2\n1409286144\n", got "2\n1410065408\n"
--- FAIL: TestE2E_LONGPROD (0.09s)
=== RUN   TestE2E_LONGPRODWithCarry
--- PASS: TestE2E_LONGPRODWithCarry (0.08s)
=== RUN   TestE2E_LONGDIV
--- PASS: TestE2E_LONGDIV (0.09s)
=== RUN   TestE2E_LONGDIVLargeValue
--- PASS: TestE2E_LONGDIVLargeValue (0.09s)
=== RUN   TestE2E_LONGSUM
--- PASS: TestE2E_LONGSUM (0.09s)
=== RUN   TestE2E_LONGSUMOverflow
    e2e_intrinsics_test.go:114: expected "0\n-2\n", got "1\n-2\n"
--- FAIL: TestE2E_LONGSUMOverflow (0.08s)
=== RUN   TestE2E_LONGDIFF
--- PASS: TestE2E_LONGDIFF (0.08s)
=== RUN   TestE2E_LONGDIFFBorrow
--- PASS: TestE2E_LONGDIFFBorrow (0.09s)
=== RUN   TestE2E_NORMALISE
--- PASS: TestE2E_NORMALISE (0.08s)
=== RUN   TestE2E_NORMALISEZero
--- PASS: TestE2E_NORMALISEZero (0.08s)
=== RUN   TestE2E_SHIFTRIGHT
--- PASS: TestE2E_SHIFTRIGHT (0.08s)
=== RUN   TestE2E_SHIFTLEFT
--- PASS: TestE2E_SHIFTLEFT (0.08s)
=== RUN   TestE2E_SHIFTLEFTCrossWord
--- PASS: TestE2E_SHIFTLEFTCrossWord (0.09s)
FAIL
FAIL	github.com/codeassociates/occam2go/codegen	1.109s
FAIL

Two intrinsic test failures — my expected values were wrong. Let me fix them.

For TestE2E_LONGPROD: 100000 * 100000 = 10,000,000,000. On a 64-bit system, int is 64-bit, but the intrinsics use uint32 casting. Let me recalculate.

Actually the issue is that 100000 doesn't fit in a uint32 as expected — wait, 100000 is well within uint32 range. Let me recalculate: uint32(100000) * uint32(100000) = 10,000,000,000. In uint64: 10000000000 = 0x2_540B_E400. hi = int(int32(uint32(0x2))) = 2, lo = int(int32(uint32(0x540BE400))) = int(int32(1409286144)) = 1409286144.

But the actual output is 1410065408. Let me check... Actually 100000 in occam might be parsed as a Go int which is 64-bit. The LONGPROD casts to uint32(a) first. uint32(100000) = 100000. uint64(100000) * uint64(100000) = 10000000000. 10000000000 in hex is 0x254_0BE_400. uint32(10000000000 >> 32) = uint32(2) = 2. uint32(10000000000) = uint32(0x540BE400) = 1409286144.

Hmm, but we're getting 1410065408 = 0x54000000. That's different. Let me actually compute what the Go code does.

Actually wait — the issue might be that 100000 is being treated differently. Let me just run a quick check to verify what LONGPROD actually produces.

$ Bash
Verify LONGPROD arithmetic
cat <<'EOF' | go run /dev/stdin
package main

import "fmt"

func main() {
    a, b, c := 100000, 100000, 0
    r := uint64(uint32(a))*uint64(uint32(b)) + uint64(uint32(c))
    hi := int(int32(uint32(r >> 32)))
    lo := int(int32(uint32(r)))
    fmt.Println(hi, lo)
    fmt.Printf("r = %d, hex = %x\n", r, r)
}
EOF
Exit code 1
directory /dev/stdin outside main module or its selected dependencies

directory /dev/stdin outside main module or its selected dependencies
$ Bash
Verify LONGPROD and LONGSUM arithmetic
cd /tmp && cat > longprod_test.go << 'EOF'
package main

import "fmt"

func main() {
    a, b, c := 100000, 100000, 0
    r := uint64(uint32(a))*uint64(uint32(b)) + uint64(uint32(c))
    hi := int(int32(uint32(r >> 32)))
    lo := int(int32(uint32(r)))
    fmt.Println(hi)
    fmt.Println(lo)
    fmt.Printf("r = %d, hex = %x\n", r, r)

    // Also verify LONGSUM overflow
    a2, b2, c2 := 2147483647, 2147483647, 0
    r2 := uint64(uint32(a2)) + uint64(uint32(b2)) + uint64(uint32(c2))
    hi2 := int(int32(uint32(r2 >> 32)))
    lo2 := int(int32(uint32(r2)))
    fmt.Println(hi2)
    fmt.Println(lo2)
}
EOF
go run longprod_test.go
Exit code 1
go: cannot run *_test.go files (longprod_test.go)

go: cannot run *_test.go files (longprod_test.go)
$ Bash
Verify LONGPROD and LONGSUM arithmetic
cd /tmp && cat > longprod_check.go << 'EOF'
package main

import "fmt"

func main() {
    a, b, c := 100000, 100000, 0
    r := uint64(uint32(a))*uint64(uint32(b)) + uint64(uint32(c))
    hi := int(int32(uint32(r >> 32)))
    lo := int(int32(uint32(r)))
    fmt.Println(hi)
    fmt.Println(lo)

    // LONGSUM overflow
    a2, b2, c2 := 2147483647, 2147483647, 0
    r2 := uint64(uint32(a2)) + uint64(uint32(b2)) + uint64(uint32(c2))
    hi2 := int(int32(uint32(r2 >> 32)))
    lo2 := int(int32(uint32(r2)))
    fmt.Println(hi2)
    fmt.Println(lo2)
}
EOF
go run longprod_check.go
2
1410065408
0
-2
Shell cwd was reset to /home/david/projects/code-associates/occam2go
Thinking

So the correct answers are: - LONGPROD(100000, 100000, 0) → hi=2, lo=1410065408 - LONGSUM(MOSTPOS INT, MOSTPOS INT, 0) → hi=0, lo=-2

Wait, for LONGSUM: uint32(2147483647) + uint32(2147483647) + uint32(0) = 0x7FFFFFFF + 0x7FFFFFFF = 0xFFFFFFFE = 4294967294. In uint64 that's still 4294967294, which is < 2^32, so r >> 32 = 0.

Converting that back to a signed int gives -2, so hi=0, lo=-2 is correct. But the test expected "0\n-2\n" and got "1\n-2\n", which means the hi value is wrong. I think the issue is that MOSTPOS INT in Go on a 64-bit system is actually math.MaxInt64, not the 32-bit max value, so I need to reconsider what values are being passed in. I see the issue now—I'm getting confused with the bit widths. Let me recalculate the LONGPROD test case more carefully. When I multiply 100000 by 100000, I get 10 billion, which in hex is 0x2540BE400. But the output shows lo as 1410065408, which is 0x54000000, so I need to figure out where that's coming from and verify my arithmetic.

Actually, I've confirmed it now: 10 billion equals 2 times 2^32 plus 1410065408, so the lo value is correct. My earlier calculation of 1409286144 was wrong. Now I need to move on to the LONGSUM test case and figure out what happens when I cast math.MaxInt64 to uint32.

I see — my expected values were wrong. Let me fix the two failing tests.

✏️ Edit e2e_intrinsics_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_intrinsics_test.go
	// LONGPROD(a, b, c) = a*b+c as 64-bit, returns (hi, lo)
	// 100000 * 100000 + 0 = 10000000000
	// 10000000000 = 0x2_540B_E400
	// hi = 2, lo = 0x540BE400 = 1409286144
	occam := `PROC main()
  INT hi, lo:
  SEQ
    hi, lo := LONGPROD(100000, 100000, 0)
    print.int(hi)
    print.int(lo)
:
`
	output := transpileCompileRun(t, occam)
	expected := "2\n1409286144\n"
+
	// LONGPROD(a, b, c) = a*b+c as 64-bit, returns (hi, lo)
	// 100000 * 100000 + 0 = 10000000000
	// 10000000000 = 2 * 2^32 + 1410065408
	// hi = 2, lo = 1410065408
	occam := `PROC main()
  INT hi, lo:
  SEQ
    hi, lo := LONGPROD(100000, 100000, 0)
    print.int(hi)
    print.int(lo)
:
`
	output := transpileCompileRun(t, occam)
	expected := "2\n1410065408\n"
The file /home/david/projects/code-associates/occam2go/codegen/e2e_intrinsics_test.go has been updated successfully.
✏️ Edit e2e_intrinsics_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_intrinsics_test.go
func TestE2E_LONGSUMOverflow(t *testing.T) {
	// LONGSUM with overflow: MOSTPOS INT + MOSTPOS INT + 0
	// In uint32: 0x7FFFFFFF + 0x7FFFFFFF = 0xFFFFFFFE → carry=0, result = -2 (signed)
	// Actually for 32-bit: 2147483647+2147483647 = 4294967294 = 0xFFFFFFFE
	// hi=0 (no carry past 32 bits), lo = int32(0xFFFFFFFE) = -2
	occam := `PROC main()
  INT carry, sum:
  SEQ
    carry, sum := LONGSUM(MOSTPOS INT, MOSTPOS INT, 0)
    print.int(carry)
    print.int(sum)
:
`
	output := transpileCompileRun(t, occam)
	expected := "0\n-2\n"
+
func TestE2E_LONGSUMOverflow(t *testing.T) {
	// LONGSUM with overflow using smaller values that fit cleanly in uint32
	// LONGSUM(0xFFFFFFFF, 1, 0): uint32 max + 1 = 0x1_0000_0000
	// hi (carry) = 1, lo = 0
	occam := `PROC main()
  INT carry, sum:
  SEQ
    carry, sum := LONGSUM(-1, 1, 0)
    print.int(carry)
    print.int(sum)
:
`
	output := transpileCompileRun(t, occam)
	// uint32(-1) = 0xFFFFFFFF, uint32(1) = 1, sum = 0x100000000
	// carry = 1, sum = 0
	expected := "1\n0\n"
The file /home/david/projects/code-associates/occam2go/codegen/e2e_intrinsics_test.go has been updated successfully.

Now let me run the intrinsics tests again and the other new test files in parallel.

$ Bash
Run intrinsics tests after fixes
go test ./codegen/ -run "TestE2E_LONG|TestE2E_NORMALISE|TestE2E_SHIFT" -v -count=1 2>&1 | tail -30
=== RUN   TestE2E_LONGPROD
--- PASS: TestE2E_LONGPROD (0.08s)
=== RUN   TestE2E_LONGPRODWithCarry
--- PASS: TestE2E_LONGPRODWithCarry (0.08s)
=== RUN   TestE2E_LONGDIV
--- PASS: TestE2E_LONGDIV (0.08s)
=== RUN   TestE2E_LONGDIVLargeValue
--- PASS: TestE2E_LONGDIVLargeValue (0.08s)
=== RUN   TestE2E_LONGSUM
--- PASS: TestE2E_LONGSUM (0.08s)
=== RUN   TestE2E_LONGSUMOverflow
--- PASS: TestE2E_LONGSUMOverflow (0.08s)
=== RUN   TestE2E_LONGDIFF
--- PASS: TestE2E_LONGDIFF (0.08s)
=== RUN   TestE2E_LONGDIFFBorrow
--- PASS: TestE2E_LONGDIFFBorrow (0.08s)
=== RUN   TestE2E_NORMALISE
--- PASS: TestE2E_NORMALISE (0.08s)
=== RUN   TestE2E_NORMALISEZero
--- PASS: TestE2E_NORMALISEZero (0.08s)
=== RUN   TestE2E_SHIFTRIGHT
--- PASS: TestE2E_SHIFTRIGHT (0.08s)
=== RUN   TestE2E_SHIFTLEFT
--- PASS: TestE2E_SHIFTLEFT (0.09s)
=== RUN   TestE2E_SHIFTLEFTCrossWord
--- PASS: TestE2E_SHIFTLEFTCrossWord (0.08s)
PASS
ok  	github.com/codeassociates/occam2go/codegen	1.054s

All intrinsics tests pass. Now let me run the other new test files.

$ Bash
Run RETYPES e2e tests
go test ./codegen/ -run "TestE2E_Retypes" -v -count=1 2>&1 | tail -30
=== RUN   TestE2E_RetypesFloat32ToInt
--- PASS: TestE2E_RetypesFloat32ToInt (0.08s)
=== RUN   TestE2E_RetypesFloat32Zero
--- PASS: TestE2E_RetypesFloat32Zero (0.08s)
=== RUN   TestE2E_RetypesFloat32NegOne
--- PASS: TestE2E_RetypesFloat32NegOne (0.08s)
=== RUN   TestE2E_RetypesSameNameShadow
--- PASS: TestE2E_RetypesSameNameShadow (0.08s)
=== RUN   TestE2E_RetypesFloat64ToIntPair
--- PASS: TestE2E_RetypesFloat64ToIntPair (0.08s)
=== RUN   TestE2E_RetypesFloat64Zero
--- PASS: TestE2E_RetypesFloat64Zero (0.08s)
PASS
ok  	github.com/codeassociates/occam2go/codegen	0.485s
$ Bash
Run params e2e tests
go test ./codegen/ -run "TestE2E_Result|TestE2E_FixedSize|TestE2E_SharedType|TestE2E_ValOpenArray" -v -count=1 2>&1 | tail -30
=== RUN   TestE2E_ResultQualifier
--- PASS: TestE2E_ResultQualifier (0.08s)
=== RUN   TestE2E_ResultQualifierMultiple
--- PASS: TestE2E_ResultQualifierMultiple (0.08s)
=== RUN   TestE2E_FixedSizeArrayParam
--- PASS: TestE2E_FixedSizeArrayParam (0.08s)
=== RUN   TestE2E_SharedTypeChanParams
--- PASS: TestE2E_SharedTypeChanParams (0.08s)
=== RUN   TestE2E_SharedTypeIntParams
--- PASS: TestE2E_SharedTypeIntParams (0.08s)
=== RUN   TestE2E_ValOpenArrayByteParam
--- PASS: TestE2E_ValOpenArrayByteParam (0.08s)
PASS
ok  	github.com/codeassociates/occam2go/codegen	0.487s
$ Bash
Run strings/print e2e tests
go test ./codegen/ -run "TestE2E_ValByteArray|TestE2E_Print|TestE2E_String" -v -count=1 2>&1 | tail -30
=== RUN   TestE2E_PrintInt
--- PASS: TestE2E_PrintInt (0.08s)
=== RUN   TestE2E_StringToByteSliceWrapping
--- PASS: TestE2E_StringToByteSliceWrapping (0.08s)
=== RUN   TestE2E_ValByteArrayAbbreviation
--- PASS: TestE2E_ValByteArrayAbbreviation (0.08s)
=== RUN   TestE2E_PrintString
--- PASS: TestE2E_PrintString (0.08s)
=== RUN   TestE2E_PrintNewline
--- PASS: TestE2E_PrintNewline (0.08s)
=== RUN   TestE2E_PrintStringAndNewline
--- PASS: TestE2E_PrintStringAndNewline (0.08s)
=== RUN   TestE2E_StringWithEscapes
--- PASS: TestE2E_StringWithEscapes (0.08s)
PASS
ok  	github.com/codeassociates/occam2go/codegen	0.563s
$ Bash
Run misc e2e tests
go test ./codegen/ -run "TestE2E_Skip|TestE2E_Stop|TestE2E_Modulo|TestE2E_Alt.*Guard|TestE2E_AltWith.*Guard|TestE2E_MostNegReal|TestE2E_MostPosReal|TestE2E_ShorthandSlice|TestE2E_GoReserved|TestE2E_MultiLine|TestE2E_Negative|TestE2E_Not|TestE2E_Logical|TestE2E_NestedIf|TestE2E_While.*Break|TestE2E_Case.*Multiple|TestE2E_Equal|TestE2E_CompileOnly|TestE2E_NestedReplicatedSeq$|TestE2E_ArraySlice|TestE2E_FunctionCall.*Cond|TestE2E_Recursive|TestE2E_MultiLineProc|TestE2E_VetOutput" -v -count=1 2>&1 | tail -80
--- PASS: TestE2E_ModuloOperator (0.08s)
=== RUN   TestE2E_ModuloInExpression
--- PASS: TestE2E_ModuloInExpression (0.08s)
=== RUN   TestE2E_AltWithBooleanGuard
    e2e_misc_test.go:164: execution failed: exit status 2
        Output: fatal error: all goroutines are asleep - deadlock!
        
        goroutine 1 [sync.WaitGroup.Wait]:
        sync.runtime_SemacquireWaitGroup(0x35b62fee210?, 0xe0?)
        	/usr/local/go/src/runtime/sema.go:114 +0x2e
        sync.(*WaitGroup).Wait(0x35b62f20110)
        	/usr/local/go/src/sync/waitgroup.go:206 +0x85
        main.main()
        	/tmp/occam2go-test-4219477256/main.go:36 +0x14f
        
        goroutine 7 [chan send]:
        main.main.func1()
        	/tmp/occam2go-test-4219477256/main.go:20 +0x5b
        created by main.main in goroutine 1
        	/tmp/occam2go-test-4219477256/main.go:18 +0xc9
        
        goroutine 8 [select]:
        main.main.func2()
        	/tmp/occam2go-test-4219477256/main.go:29 +0xad
        created by main.main in goroutine 1
        	/tmp/occam2go-test-4219477256/main.go:23 +0x145
--- FAIL: TestE2E_AltWithBooleanGuard (0.08s)
=== RUN   TestE2E_AltWithTrueGuard
--- PASS: TestE2E_AltWithTrueGuard (0.08s)
=== RUN   TestE2E_MostNegReal32
--- PASS: TestE2E_MostNegReal32 (0.08s)
=== RUN   TestE2E_MostPosReal32
--- PASS: TestE2E_MostPosReal32 (0.08s)
=== RUN   TestE2E_MostNegReal64
--- PASS: TestE2E_MostNegReal64 (0.08s)
=== RUN   TestE2E_MostPosReal64
--- PASS: TestE2E_MostPosReal64 (0.08s)
=== RUN   TestE2E_ShorthandSliceFromZero
--- PASS: TestE2E_ShorthandSliceFromZero (0.08s)
=== RUN   TestE2E_GoReservedWordEscaping
--- PASS: TestE2E_GoReservedWordEscaping (0.08s)
=== RUN   TestE2E_GoReservedWordByte
--- PASS: TestE2E_GoReservedWordByte (0.08s)
=== RUN   TestE2E_MultiLineExpression
--- PASS: TestE2E_MultiLineExpression (0.08s)
=== RUN   TestE2E_MultiLineParenExpression
--- PASS: TestE2E_MultiLineParenExpression (0.08s)
=== RUN   TestE2E_NegativeIntLiteral
--- PASS: TestE2E_NegativeIntLiteral (0.08s)
=== RUN   TestE2E_NotOperator
--- PASS: TestE2E_NotOperator (0.08s)
=== RUN   TestE2E_LogicalAndOr
--- PASS: TestE2E_LogicalAndOr (0.08s)
=== RUN   TestE2E_NestedIfInSeq
--- PASS: TestE2E_NestedIfInSeq (0.08s)
=== RUN   TestE2E_WhileWithBreakCondition
--- PASS: TestE2E_WhileWithBreakCondition (0.08s)
=== RUN   TestE2E_CaseWithMultipleArms
--- PASS: TestE2E_CaseWithMultipleArms (0.08s)
=== RUN   TestE2E_EqualNotEqual
--- PASS: TestE2E_EqualNotEqual (0.08s)
=== RUN   TestE2E_CompileOnly_StopInProc
--- PASS: TestE2E_CompileOnly_StopInProc (0.04s)
=== RUN   TestE2E_NestedReplicatedSeq
--- PASS: TestE2E_NestedReplicatedSeq (0.08s)
=== RUN   TestE2E_ArraySliceAssignment
--- PASS: TestE2E_ArraySliceAssignment (0.08s)
=== RUN   TestE2E_FunctionCallInCondition
--- PASS: TestE2E_FunctionCallInCondition (0.08s)
=== RUN   TestE2E_RecursiveFunction
--- PASS: TestE2E_RecursiveFunction (0.08s)
=== RUN   TestE2E_MultiLineProcParams
--- PASS: TestE2E_MultiLineProcParams (0.08s)
=== RUN   TestE2E_VetOutputClean
--- PASS: TestE2E_VetOutputClean (0.12s)
=== RUN   TestE2E_MultiLineBooleanIF
--- PASS: TestE2E_MultiLineBooleanIF (0.08s)
FAIL
FAIL	github.com/codeassociates/occam2go/codegen	2.593s
FAIL

One failure: TestE2E_AltWithBooleanGuard deadlocks. The issue is that when the boolean guard is FALSE, the ALT case is disabled but the channel still has a value sent on it. Let me look at the generated code to understand the problem and fix the test.

$ Bash
Check generated code for boolean ALT guard
echo 'SEQ
  CHAN OF INT c1:
  CHAN OF INT c2:
  INT result:
  BOOL allow:
  allow := FALSE
  PAR
    SEQ
      c1 ! 10
      c2 ! 42
    ALT
      allow & c1 ? result
        SKIP
      TRUE & c2 ? result
        SKIP
  print.int(result)' | ./occam2go /dev/stdin 2>&1
Exit code 1
Parse errors:
  line 9: unexpected token: \
  line 9: unexpected token: !
  line 9: unexpected token: INT
  line 10: unexpected token: \
  line 10: unexpected token: !
  line 10: unexpected token: INT

Parse errors:
  line 9: unexpected token: \
  line 9: unexpected token: !
  line 9: unexpected token: INT
  line 10: unexpected token: \
  line 10: unexpected token: !
  line 10: unexpected token: INT
$ Bash
Check generated code for boolean ALT guard via file
cat > /tmp/alt_guard_test.occ << 'EOF'
SEQ
  CHAN OF INT c1:
  CHAN OF INT c2:
  INT result:
  BOOL allow:
  allow := FALSE
  PAR
    SEQ
      c1 ! 10
      c2 ! 42
    ALT
      allow & c1 ? result
        SKIP
      TRUE & c2 ? result
        SKIP
  print.int(result)
EOF
./occam2go -o /tmp/alt_guard_test.go /tmp/alt_guard_test.occ 2>&1 && cat /tmp/alt_guard_test.go
package main

import (
	"fmt"
	"sync"
)

func main() {
	c1 := make(chan int)
	c2 := make(chan int)
	var result int
	_ = result
	var allow bool
	_ = allow
	allow = false
	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		defer wg.Done()
		c1 <- 10
		c2 <- 42
	}()
	go func() {
		defer wg.Done()
		var _alt0 chan int = nil
		if allow { _alt0 = c1 }
		var _alt1 chan int = nil
		if true { _alt1 = c2 }
		select {
		case result = <-_alt0:
			// SKIP
		case result = <-_alt1:
			// SKIP
		}
	}()
	wg.Wait()
	fmt.Println(result)
}
Thinking

The generated code looks correct. The issue is that the ALT correctly disables c1 (because allow = false), so only c2 will be selected. But the problem is that after the ALT reads from c2, the first goroutine is stuck trying to send to c1 (it already sent to c2 successfully, but then tries to send to c1 which nobody reads from). This causes a deadlock.

The test program is fundamentally flawed - the sender goroutine sends to both c1 and c2, but only one channel gets read. I need to redesign the test so that the disabled channel's sender doesn't get stuck.

I should restructure this so the sender only attempts to send on the channel that will actually be selected by the ALT. That way, when the guard condition disables c1, the sender won't try to use it at all.

The test logic is wrong — the sender sends on both c1 and c2, but only c2 is read (c1's guard is FALSE). The c1 sender blocks forever. Let me fix the test.

✏️ Edit e2e_misc_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_misc_test.go
func TestE2E_AltWithBooleanGuard(t *testing.T) {
	// ALT with boolean guard: TRUE & c ? x
	occam := `SEQ
  CHAN OF INT c1:
  CHAN OF INT c2:
  INT result:
  BOOL allow:
  allow := FALSE
  PAR
    SEQ
      c1 ! 10
      c2 ! 42
    ALT
      allow & c1 ? result
        SKIP
      TRUE & c2 ? result
        SKIP
  print.int(result)
`
+
func TestE2E_AltWithBooleanGuard(t *testing.T) {
	// ALT with boolean guard: FALSE guard disables a channel
	// Only send on c2 since c1's guard is FALSE and won't be selected
	occam := `SEQ
  CHAN OF INT c1:
  CHAN OF INT c2:
  INT result:
  BOOL allow:
  allow := FALSE
  PAR
    c2 ! 42
    ALT
      allow & c1 ? result
        SKIP
      TRUE & c2 ? result
        SKIP
  print.int(result)
`
The file /home/david/projects/code-associates/occam2go/codegen/e2e_misc_test.go has been updated successfully.
$ Bash
Run fixed ALT guard tests
go test ./codegen/ -run "TestE2E_AltWith" -v -count=1 2>&1
=== RUN   TestE2E_AltWithBody
--- PASS: TestE2E_AltWithBody (0.08s)
=== RUN   TestE2E_AltWithBooleanGuard
--- PASS: TestE2E_AltWithBooleanGuard (0.08s)
=== RUN   TestE2E_AltWithTrueGuard
--- PASS: TestE2E_AltWithTrueGuard (0.08s)
PASS
ok  	github.com/codeassociates/occam2go/codegen	0.244s

Now let me also check the STOP test (it has a timeout mechanism):

$ Bash
Run STOP test
go test ./codegen/ -run "TestE2E_StopReached" -v -count=1 -timeout 30s 2>&1
=== RUN   TestE2E_StopReached
--- PASS: TestE2E_StopReached (0.08s)
PASS
ok  	github.com/codeassociates/occam2go/codegen	0.086s

Now let me run the entire test suite to make sure nothing is broken.

$ Bash
Run full test suite
go test ./... -count=1 -timeout 300s 2>&1
?   	github.com/codeassociates/occam2go	[no test files]
?   	github.com/codeassociates/occam2go/ast	[no test files]
ok  	github.com/codeassociates/occam2go/codegen	15.488s
ok  	github.com/codeassociates/occam2go/lexer	0.002s
ok  	github.com/codeassociates/occam2go/modgen	0.002s
ok  	github.com/codeassociates/occam2go/parser	0.003s
ok  	github.com/codeassociates/occam2go/preproc	0.003s

All tests pass across the entire codebase. Let me update the task status and give a summary.

TaskUpdate
{
  "taskId": "8",
  "status": "completed"
}
Updated task #8 status

All done. Here's what was added:

New Test Files

codegen/e2e_intrinsics_test.go — 13 tests

Transputer intrinsics (previously zero coverage): - LONGPROD (basic + with carry) - LONGDIV (basic + large roundtrip with LONGPROD) - LONGSUM (basic + overflow) - LONGDIFF (basic + borrow) - NORMALISE (basic + zero) - SHIFTRIGHT, SHIFTLEFT (basic + cross-word shift)

codegen/e2e_retypes_test.go — 6 tests

RETYPES bit reinterpretation (previously parser-only): - float32→int (1.0, 0.0, -1.0) - Same-name shadowing (VAL INT X RETYPES X :) - float64→int pair (1.0, 0.0)

codegen/e2e_params_test.go — 6 tests

Proc parameter variants (previously zero coverage): - RESULT qualifier (single + multiple) - Fixed-size array params [2]INT - Shared-type channel params CHAN OF INT input?, output! - Shared-type int params VAL INT a, b - VAL []BYTE param with string→[]byte wrapping

codegen/e2e_strings_test.go — 5 tests

String/print operations (previously zero e2e): - VAL []BYTE s IS "hello": abbreviation - print.string - print.newline - Combined string printing - String with escape sequences

codegen/e2e_misc_test.go — 24 tests

Coverage for many smaller gaps: - SKIP (standalone + in PAR) - STOP (reached — verifies deadlock/non-zero exit) - Modulo operator \ (basic + in expression) - ALT with boolean guards (FALSE guard + TRUE guard) - MOSTNEG/MOSTPOS for REAL32 and REAL64 (4 tests) - Shorthand slice [arr FOR m] - String→[]byte wrapping - Go reserved word escaping (len, byte) - Multi-line expressions (continuation + paren) - Negative int literal, NOT, AND/OR - Nested IF, WHILE loop, CASE multi-arm - = / <> operators - STOP in proc (compile-only check) - Nested replicated SEQ - Array slice assignment - Function call in IF condition - Recursive function - Multi-line proc params - go vet cleanness check

lexer/lexer_test2_test.go — 15 tests

Lexer-level coverage gaps: - All 33+ keywords (previously only 11 tested) - Paren depth suppression of INDENT/DEDENT - Bracket depth suppression - Continuation operator line continuation (+ AND) - String literal tokenization - String escape preservation - Byte literal + escape - Send/receive !/? tokens - Ampersand & token - Semicolon ; token - Nested paren depth tracking - Mixed paren/bracket depth - Line/column tracking

Summary

Area Before Added Total
Lexer tests 9 15 24
E2E tests ~124 54 ~178
Total new tests 69

All 69 new tests pass alongside all existing tests.

Thank you. Can you write that report of coverage improvement to a markdown file (to be committed to the repo) so we can keep track of this work?

📝 Write TEST_COVERAGE_IMPROVEMENTS.md
/home/david/projects/code-associates/occam2go/TEST_COVERAGE_IMPROVEMENTS.md
# Test Coverage Improvements

## Overview

This document tracks additions to the test suite that close coverage gaps identified by analyzing the "What's Implemented" feature list against existing tests.

## 2026-02-20: 69 New Tests

### Gap Analysis

Prior to this work, the test suite had ~124 e2e tests, ~49 codegen unit tests, ~80 parser tests, and ~9 lexer tests. Several implemented features had zero or near-zero test coverage:

- Transputer intrinsics (LONGPROD, LONGDIV, LONGSUM, LONGDIFF, NORMALISE, SHIFTRIGHT, SHIFTLEFT) — zero tests at any level
- RETYPES bit reinterpretation — parser tests only, no codegen or e2e verification
- RESULT qualifier on proc params — completely untested
- Fixed-size array params `[n]TYPE` — completely untested
- Shared-type channel params `PROC f(CHAN INT a?, b?)` — completely untested
- `VAL []BYTE s IS "hi":` abbreviation — completely untested
- ALT with boolean guards — parser test only, no e2e
- MOSTNEG/MOSTPOS for REAL32/REAL64 — codegen unit only, no e2e
- Modulo operator `\` — lexer tokenization only
- `print.string` / `print.newline` — no e2e execution
- Lexer paren/bracket depth suppression — only exercised indirectly
- Lexer continuation-operator line joining — only exercised indirectly
- Most keywords — only 11 of 33+ had lexer-level tests

### New Test Files

#### `codegen/e2e_intrinsics_test.go` — 13 tests

| Test | Feature |
|------|---------|
| `TestE2E_LONGPROD` | Basic 64-bit multiply |
| `TestE2E_LONGPRODWithCarry` | Multiply with carry addend |
| `TestE2E_LONGDIV` | Basic 64-bit divide |
| `TestE2E_LONGDIVLargeValue` | Roundtrip with LONGPROD |
| `TestE2E_LONGSUM` | Basic 64-bit add |
| `TestE2E_LONGSUMOverflow` | Addition with carry output |
| `TestE2E_LONGDIFF` | Basic 64-bit subtract |
| `TestE2E_LONGDIFFBorrow` | Subtraction with borrow |
| `TestE2E_NORMALISE` | Leading-zero normalization |
| `TestE2E_NORMALISEZero` | Zero input edge case |
| `TestE2E_SHIFTRIGHT` | 64-bit right shift |
| `TestE2E_SHIFTLEFT` | 64-bit left shift |
| `TestE2E_SHIFTLEFTCrossWord` | Shift across word boundary |

#### `codegen/e2e_retypes_test.go` — 6 tests

| Test | Feature |
|------|---------|
| `TestE2E_RetypesFloat32ToInt` | `VAL INT bits RETYPES x :` (float32 1.0) |
| `TestE2E_RetypesFloat32Zero` | float32 0.0 bit pattern |
| `TestE2E_RetypesFloat32NegOne` | float32 -1.0 bit pattern |
| `TestE2E_RetypesSameNameShadow` | `VAL INT X RETYPES X :` (param rename) |
| `TestE2E_RetypesFloat64ToIntPair` | `VAL [2]INT X RETYPES X :` (float64 1.0) |
| `TestE2E_RetypesFloat64Zero` | float64 0.0 split into two words |

#### `codegen/e2e_params_test.go` — 6 tests

| Test | Feature |
|------|---------|
| `TestE2E_ResultQualifier` | `PROC f(RESULT INT x)` |
| `TestE2E_ResultQualifierMultiple` | Multiple RESULT params |
| `TestE2E_FixedSizeArrayParam` | `PROC f([2]INT arr)` → pointer |
| `TestE2E_SharedTypeChanParams` | `PROC f(CHAN OF INT input?, output!)` |
| `TestE2E_SharedTypeIntParams` | `PROC f(VAL INT a, b, INT result)` |
| `TestE2E_ValOpenArrayByteParam` | `PROC f(VAL []BYTE s)` with string arg |

#### `codegen/e2e_strings_test.go` — 5 tests

| Test | Feature |
|------|---------|
| `TestE2E_ValByteArrayAbbreviation` | `VAL []BYTE s IS "hello":` |
| `TestE2E_PrintString` | `print.string("hello world")` |
| `TestE2E_PrintNewline` | `print.newline()` |
| `TestE2E_PrintStringAndNewline` | Combined string printing |
| `TestE2E_StringWithEscapes` | Occam `*t` escape in string |

#### `codegen/e2e_misc_test.go` — 24 tests

| Test | Feature |
|------|---------|
| `TestE2E_SkipStatement` | SKIP as standalone no-op |
| `TestE2E_SkipInPar` | SKIP in a PAR branch |
| `TestE2E_StopReached` | STOP causes non-zero exit (deadlock) |
| `TestE2E_ModuloOperator` | `\` → `%` |
| `TestE2E_ModuloInExpression` | Modulo in compound expression |
| `TestE2E_AltWithBooleanGuard` | FALSE guard disables ALT branch |
| `TestE2E_AltWithTrueGuard` | TRUE guard enables ALT branch |
| `TestE2E_MostNegReal32` | `MOSTNEG REAL32` is negative |
| `TestE2E_MostPosReal32` | `MOSTPOS REAL32` is positive |
| `TestE2E_MostNegReal64` | `MOSTNEG REAL64` is negative |
| `TestE2E_MostPosReal64` | `MOSTPOS REAL64` is positive |
| `TestE2E_ShorthandSliceFromZero` | `[arr FOR 3]` (FROM 0 implied) |
| `TestE2E_StringToByteSliceWrapping` | String literal → `[]byte()` for `[]BYTE` param |
| `TestE2E_GoReservedWordEscaping` | Variable named `len` works |
| `TestE2E_GoReservedWordByte` | Variable named `byte` works |
| `TestE2E_MultiLineExpression` | Continuation operator at line end |
| `TestE2E_MultiLineParenExpression` | Expression inside parens across lines |
| `TestE2E_NegativeIntLiteral` | Unary minus |
| `TestE2E_NotOperator` | `NOT TRUE` |
| `TestE2E_LogicalAndOr` | `AND` / `OR` operators |
| `TestE2E_NestedIfInSeq` | Nested IF with variable declarations |
| `TestE2E_WhileWithBreakCondition` | WHILE counting to target |
| `TestE2E_CaseWithMultipleArms` | CASE with 4 branches |
| `TestE2E_EqualNotEqual` | `=` and `<>` operators |
| `TestE2E_CompileOnly_StopInProc` | STOP in proc compiles cleanly |
| `TestE2E_NestedReplicatedSeq` | Nested `SEQ i = 0 FOR 3` loops |
| `TestE2E_ArraySliceAssignment` | `[dst FROM 1 FOR 3] := src` |
| `TestE2E_FunctionCallInCondition` | `BOOL FUNCTION` as IF condition |
| `TestE2E_RecursiveFunction` | Recursive factorial |
| `TestE2E_MultiLineProcParams` | Multi-line proc parameter list |
| `TestE2E_VetOutputClean` | `go vet` passes on generated code |

#### `lexer/lexer_test2_test.go` — 15 tests

| Test | Feature |
|------|---------|
| `TestAllKeywords` | All 33+ keywords tokenize correctly |
| `TestParenDepthSuppressesIndent` | No INDENT/DEDENT inside `(...)` |
| `TestBracketDepthSuppressesIndent` | No INDENT/DEDENT inside `[...]` |
| `TestContinuationOperator` | `+` at line end joins lines |
| `TestContinuationAND` | `AND` at line end joins lines |
| `TestStringLiteral` | `"hello world"` → STRING token |
| `TestStringEscapeSequences` | `*n` preserved raw by lexer |
| `TestByteLiteralToken` | `'A'` → BYTE_LIT token |
| `TestByteLiteralEscapeToken` | `'*n'` → BYTE_LIT with raw escape |
| `TestSendReceiveTokens` | `!` → SEND, `?` → RECEIVE |
| `TestAmpersandToken` | `&` → AMPERSAND |
| `TestSemicolonToken` | `;` → SEMICOLON |
| `TestNestedParenDepth` | Nested `((` tracks depth correctly |
| `TestMixedParenBracketDepth` | `arr[(1 + 2)]` mixed nesting |
| `TestLineAndColumnTracking` | Token line/column numbers |

### Summary

| Area | Before | Added | After |
|------|--------|-------|-------|
| Lexer unit tests | 9 | 15 | 24 |
| Parser unit tests | 80 | 0 | 80 |
| Codegen unit tests | 49 | 0 | 49 |
| E2E tests | ~124 | 54 | ~178 |
| Preprocessor tests | 22 | 0 | 22 |
| Modgen tests | 5 | 0 | 5 |
| **Total** | **~289** | **69** | **~358** |

### Remaining Gaps

Features with limited or indirect-only coverage that could benefit from future tests:

- Parser-level tests for SKIP, STOP, `VAL []BYTE` abbreviation, RESULT qualifier, fixed-size array params, transputer intrinsic calls, variant receive `c ? CASE`, timer ALT arm
- Codegen unit tests for RETYPES output, intrinsic helper emission, sequential/variant protocol send/receive code, `VAL []BYTE` abbreviation output
- `print.nl` (alias for `print.newline`, if supported)
- PRI ALT / PRI PAR (not yet implemented)
File created successfully at: /home/david/projects/code-associates/occam2go/TEST_COVERAGE_IMPROVEMENTS.md

Done — written to TEST_COVERAGE_IMPROVEMENTS.md in the repo root. It includes the gap analysis, every new test listed by file with what it covers, the before/after count summary, and notes on remaining gaps for future work.

Thank you. Please create a PR for this work.

$ Bash
Show working tree status
git status
On branch main
Your branch is up to date with 'origin/main'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	TEST_COVERAGE_IMPROVEMENTS.md
	codegen/e2e_intrinsics_test.go
	codegen/e2e_misc_test.go
	codegen/e2e_params_test.go
	codegen/e2e_retypes_test.go
	codegen/e2e_strings_test.go
	lexer/lexer_test2_test.go

nothing added to commit but untracked files present (use "git add" to track)
$ Bash
Show recent commit messages
git log --oneline -5
218d5c9 Merge pull request #29 from codeassociates/docs/codebase-size-summary
090824c Add codebase size summary to README
05e194b Merge pull request #28 from codeassociates/feat/course-module-runnable
4820ccc Enable running course module programs end-to-end
413a0f2 Merge pull request #27 from codeassociates/feat/course-module-phase2
$ Bash
Create feature branch
git checkout -b test/expand-test-coverage
Switched to a new branch 'test/expand-test-coverage'
$ Bash
Stage all new test files
git add TEST_COVERAGE_IMPROVEMENTS.md codegen/e2e_intrinsics_test.go codegen/e2e_misc_test.go codegen/e2e_params_test.go codegen/e2e_retypes_test.go codegen/e2e_strings_test.go lexer/lexer_test2_test.go
$ Bash
Commit new tests
git commit -m "$(cat <<'EOF'
Add 69 tests covering previously untested transpiler features

Systematic gap analysis identified several implemented features with zero
or near-zero test coverage. This adds e2e and lexer tests for: transputer
intrinsics (LONGPROD, LONGDIV, LONGSUM, LONGDIFF, NORMALISE, SHIFTRIGHT,
SHIFTLEFT), RETYPES bit reinterpretation, RESULT qualifier params,
fixed-size array params, shared-type params, VAL []BYTE abbreviation,
print.string/print.newline, ALT with boolean guards, MOSTNEG/MOSTPOS for
REAL32/REAL64, modulo operator, SKIP/STOP, string-to-[]byte wrapping, Go
reserved word escaping, multi-line expressions, and lexer paren/bracket
depth suppression.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"
7 files changed, 1700 insertions(+)
 create mode 100644 TEST_COVERAGE_IMPROVEMENTS.md
 create mode 100644 codegen/e2e_intrinsics_test.go
 create mode 100644 codegen/e2e_misc_test.go
 create mode 100644 codegen/e2e_params_test.go
 create mode 100644 codegen/e2e_retypes_test.go
 create mode 100644 codegen/e2e_strings_test.go
 create mode 100644 lexer/lexer_test2_test.go
$ Bash
Push branch to remote
git push -u origin test/expand-test-coverage
remote: 
remote: Create a pull request for 'test/expand-test-coverage' on GitHub by visiting:        
remote:      https://github.com/codeassociates/occam2go/pull/new/test/expand-test-coverage        
remote: 
To associate-1.github.com:codeassociates/occam2go.git
 * [new branch]      test/expand-test-coverage -> test/expand-test-coverage
branch 'test/expand-test-coverage' set up to track 'origin/test/expand-test-coverage'.
$ Bash
Create pull request
gh pr create --title "Add 69 tests covering previously untested transpiler features" --body "$(cat <<'EOF'
## Summary

- Systematic gap analysis of implemented features vs existing test coverage identified 10+ feature areas with zero or near-zero tests
- Adds 54 new e2e tests across 5 new test files and 15 new lexer unit tests, bringing the total from ~289 to ~358
- All new tests pass alongside the full existing suite

## New test coverage

| File | Tests | Covers |
|------|-------|--------|
| `e2e_intrinsics_test.go` | 13 | LONGPROD, LONGDIV, LONGSUM, LONGDIFF, NORMALISE, SHIFTRIGHT, SHIFTLEFT |
| `e2e_retypes_test.go` | 6 | VAL INT RETYPES (float32→int), VAL [2]INT RETYPES (float64→int pair), same-name shadowing |
| `e2e_params_test.go` | 6 | RESULT qualifier, fixed-size array params, shared-type params, VAL []BYTE with string arg |
| `e2e_strings_test.go` | 5 | VAL []BYTE abbreviation, print.string, print.newline, string escapes |
| `e2e_misc_test.go` | 24 | SKIP, STOP, modulo, ALT boolean guards, MOSTNEG/MOSTPOS REAL32/REAL64, shorthand slice, Go reserved word escaping, multi-line expressions, recursive functions, go vet cleanness, and more |
| `lexer_test2_test.go` | 15 | All 33+ keywords, paren/bracket depth suppression, continuation operators, string/byte literals, send/receive/ampersand/semicolon tokens, line/column tracking |

See `TEST_COVERAGE_IMPROVEMENTS.md` for the full detailed breakdown.

## Test plan

- [x] `go test ./...` passes (all 358 tests)
- [x] No changes to production code — tests only
- [x] Each previously-untested feature now has at least one e2e test verifying transpile → compile → run → correct output

🤖 Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
https://github.com/codeassociates/occam2go/pull/30

PR created: https://github.com/codeassociates/occam2go/pull/30

Thank you. I've approved the PR and CI checks have passed to please go ahead and merge.

$ Bash
Merge PR #30
gh pr merge 30 --merge
$ Bash
Switch to main and pull merged changes
git checkout main && git pull
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
From associate-1.github.com:codeassociates/occam2go
   218d5c9..d21963b  main       -> origin/main
Updating 218d5c9..d21963b
Fast-forward
 TEST_COVERAGE_IMPROVEMENTS.md  | 154 ++++++++++
 codegen/e2e_intrinsics_test.go | 243 +++++++++++++++
 codegen/e2e_misc_test.go       | 682 +++++++++++++++++++++++++++++++++++++++++
 codegen/e2e_params_test.go     | 128 ++++++++
 codegen/e2e_retypes_test.go    | 121 ++++++++
 codegen/e2e_strings_test.go    |  67 ++++
 lexer/lexer_test2_test.go      | 305 ++++++++++++++++++
 7 files changed, 1700 insertions(+)
 create mode 100644 TEST_COVERAGE_IMPROVEMENTS.md
 create mode 100644 codegen/e2e_intrinsics_test.go
 create mode 100644 codegen/e2e_misc_test.go
 create mode 100644 codegen/e2e_params_test.go
 create mode 100644 codegen/e2e_retypes_test.go
 create mode 100644 codegen/e2e_strings_test.go
 create mode 100644 lexer/lexer_test2_test.go

Merged and pulled. PR #30 is on main.