Claude Code transcript - page 1/2

Implement the following plan:

Plan: Support INT16, INT32, INT64 integer types (Issue #46)

Context

The parser doesn't recognize INT16, INT32, INT64 as type keywords, causing parse failures on real-world occam programs (e.g., cast.occ, commstime.occ). These are standard occam explicit-width integer types that need the same treatment as INT, BYTE, REAL32, REAL64.

Changes

1. Lexer — lexer/token.go

Add 3 new token types and keyword entries:

  • Token enum (~line 82): Add INT16_TYPE, INT32_TYPE, INT64_TYPE after REAL64_TYPE
  • tokenNames map (~line 172): Add "INT16", "INT32", "INT64" entries
  • keywords map (~line 220): Add "INT16": INT16_TYPE, "INT32": INT32_TYPE, "INT64": INT64_TYPE

2. Parser — parser/parser.go

  • isTypeToken() (~line 1978): Add || t == lexer.INT16_TYPE || t == lexer.INT32_TYPE || t == lexer.INT64_TYPE — this single change propagates to all ~17 call sites
  • MOSTNEG/MOSTPOS parsing (~line 2795): Add !p.peekTokenIs(lexer.INT16_TYPE) && !p.peekTokenIs(lexer.INT32_TYPE) && !p.peekTokenIs(lexer.INT64_TYPE) to the guard check
  • Type conversion prefix (~line 2807): Add lexer.INT16_TYPE, lexer.INT32_TYPE, lexer.INT64_TYPE to the case list

3. AST — ast/ast.go

No changes needed — types stored as strings.

4. Codegen — codegen/codegen.go

  • occamTypeToGoBase() (~line 1400): Add cases "INT16" → "int16", "INT32" → "int32", "INT64" → "int64"
  • occamTypeToGo() (~line 1421): Add same cases
  • generateMostExpr() (~line 926): Add cases for INT16/INT32/INT64 mapping to math.MinInt16/math.MaxInt16, etc.
  • exprNeedsMath() (~line 893): Current check is return e.ExprType != "BYTE" — INT16/32/64 all need math import, so no change needed (they won't match "BYTE")

5. Tests

  • Parser unit tests (parser/parser_test.go): Add test for INT16/INT32/INT64 variable declarations and type conversions
  • Codegen unit tests (codegen/codegen_test.go): Add test for INT16/INT32/INT64 code generation
  • E2E tests (codegen/e2e_test.go or codegen/e2e_types_test.go): Add test that declares INT16/32/64 variables, uses type conversions and MOSTNEG/MOSTPOS, compiles and runs

6. Documentation

  • Update TODO.md to mark INT16/INT32/INT64 as supported

Verification

go test ./...                    # all tests pass
./occam2go -I kroc/modules/course/libsrc -D TARGET.BITS.PER.WORD=32 -o /tmp/course_out.go kroc/modules/course/libsrc/course.module
go vet /tmp/course_out.go        # course module still compiles

If you need specific details from before exiting plan mode (like exact code snippets, error messages, or content you generated), read the full transcript at: /home/david/.claude/projects/-home-david-projects-code-associates-occam2go/be722174-d55e-4926-9bb6-697f4111e01a.jsonl

Thinking

Let me implement the plan for supporting INT16, INT32, INT64 integer types. I'll start by reading the relevant files to understand the current code structure, then make all the changes.

I'll start by reading the files that need to be modified.

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→	INLINE   // INLINE (function modifier, ignored for transpilation)
    95→	PLUS_KW  // PLUS (modular addition keyword, distinct from + symbol)
    96→	MINUS_KW // MINUS (modular subtraction keyword, distinct from - symbol)
    97→	TIMES    // TIMES (modular multiplication keyword)
    98→	keyword_end
    99→)
   100→
   101→var tokenNames = map[TokenType]string{
   102→	ILLEGAL: "ILLEGAL",
   103→	EOF:     "EOF",
   104→	NEWLINE: "NEWLINE",
   105→	INDENT:  "INDENT",
   106→	DEDENT:  "DEDENT",
   107→
   108→	IDENT:    "IDENT",
   109→	INT:      "INT",
   110→	STRING:   "STRING",
   111→	BYTE_LIT: "BYTE_LIT",
   112→
   113→	ASSIGN:   ":=",
   114→	PLUS:     "+",
   115→	MINUS:    "-",
   116→	MULTIPLY: "*",
   117→	DIVIDE:   "/",
   118→	MODULO:   "\\",
   119→	EQ:       "=",
   120→	NEQ:      "<>",
   121→	LT:       "<",
   122→	GT:       ">",
   123→	LE:       "<=",
   124→	GE:       ">=",
   125→	SEND:      "!",
   126→	RECEIVE:   "?",
   127→	AMPERSAND: "&",
   128→	BITAND:    "/\\",
   129→	BITOR:     "\\/",
   130→	BITXOR:    "><",
   131→	BITNOT:    "~",
   132→	LSHIFT:    "<<",
   133→	RSHIFT:    ">>",
   134→
   135→	LPAREN:    "(",
   136→	RPAREN:    ")",
   137→	LBRACKET:  "[",
   138→	RBRACKET:  "]",
   139→	COMMA:     ",",
   140→	COLON:     ":",
   141→	SEMICOLON: ";",
   142→
   143→	SEQ:       "SEQ",
   144→	PAR:       "PAR",
   145→	ALT:       "ALT",
   146→	IF:        "IF",
   147→	CASE:      "CASE",
   148→	ELSE:      "ELSE",
   149→	WHILE:     "WHILE",
   150→	FOR:       "FOR",
   151→	FROM:      "FROM",
   152→	PROC:      "PROC",
   153→	FUNC:      "FUNC",
   154→	FUNCTION:  "FUNCTION",
   155→	VALOF:     "VALOF",
   156→	RESULT:    "RESULT",
   157→	IS:        "IS",
   158→	CHAN:      "CHAN",
   159→	OF:        "OF",
   160→	TRUE:      "TRUE",
   161→	FALSE:     "FALSE",
   162→	NOT:       "NOT",
   163→	AND:       "AND",
   164→	OR:        "OR",
   165→	SKIP:      "SKIP",
   166→	STOP:      "STOP",
   167→	INT_TYPE:  "INT",
   168→	BYTE_TYPE: "BYTE",
   169→	BOOL_TYPE: "BOOL",
   170→	REAL_TYPE:   "REAL",
   171→	REAL32_TYPE: "REAL32",
   172→	REAL64_TYPE: "REAL64",
   173→	TIMER:       "TIMER",
   174→	AFTER:    "AFTER",
   175→	VAL:       "VAL",
   176→	PROTOCOL:  "PROTOCOL",
   177→	RECORD:    "RECORD",
   178→	SIZE_KW:    "SIZE",
   179→	STEP:       "STEP",
   180→	MOSTNEG_KW: "MOSTNEG",
   181→	MOSTPOS_KW: "MOSTPOS",
   182→	INITIAL:    "INITIAL",
   183→	RETYPES:    "RETYPES",
   184→	INLINE:     "INLINE",
   185→	PLUS_KW:    "PLUS",
   186→	MINUS_KW:   "MINUS",
   187→	TIMES:      "TIMES",
   188→}
   189→
   190→var keywords = map[string]TokenType{
   191→	"SEQ":   SEQ,
   192→	"PAR":   PAR,
   193→	"ALT":   ALT,
   194→	"IF":    IF,
   195→	"CASE":  CASE,
   196→	"ELSE":  ELSE,
   197→	"WHILE": WHILE,
   198→	"FOR":   FOR,
   199→	"FROM":  FROM,
   200→	"PROC":  PROC,
   201→	"FUNC":     FUNC,
   202→	"FUNCTION": FUNCTION,
   203→	"VALOF":    VALOF,
   204→	"RESULT":   RESULT,
   205→	"IS":       IS,
   206→	"CHAN":     CHAN,
   207→	"OF":    OF,
   208→	"TRUE":  TRUE,
   209→	"FALSE": FALSE,
   210→	"NOT":   NOT,
   211→	"AND":   AND,
   212→	"OR":    OR,
   213→	"SKIP":  SKIP,
   214→	"STOP":  STOP,
   215→	"INT":   INT_TYPE,
   216→	"BYTE":  BYTE_TYPE,
   217→	"BOOL":  BOOL_TYPE,
   218→	"REAL":   REAL_TYPE,
   219→	"REAL32": REAL32_TYPE,
   220→	"REAL64": REAL64_TYPE,
   221→	"TIMER":  TIMER,
   222→	"AFTER": AFTER,
   223→	"VAL":      VAL,
   224→	"PROTOCOL": PROTOCOL,
   225→	"RECORD":   RECORD,
   226→	"SIZE":     SIZE_KW,
   227→	"STEP":     STEP,
   228→	"MOSTNEG":  MOSTNEG_KW,
   229→	"MOSTPOS":  MOSTPOS_KW,
   230→	"INITIAL":  INITIAL,
   231→	"RETYPES":  RETYPES,
   232→	"INLINE":   INLINE,
   233→	"PLUS":     PLUS_KW,
   234→	"MINUS":    MINUS_KW,
   235→	"TIMES":    TIMES,
   236→}
   237→
   238→func (t TokenType) String() string {
   239→	if name, ok := tokenNames[t]; ok {
   240→		return name
   241→	}
   242→	return "UNKNOWN"
   243→}
   244→
   245→func LookupIdent(ident string) TokenType {
   246→	if tok, ok := keywords[ident]; ok {
   247→		return tok
   248→	}
   249→	return IDENT
   250→}
   251→
   252→type Token struct {
   253→	Type    TokenType
   254→	Literal string
   255→	Line    int
   256→	Column  int
   257→}
   258→
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/codegen/codegen.go",
  "offset": 880,
  "limit": 80
}
   880→			if c.Body != nil && g.containsMostExpr(c.Body) {
   881→				return true
   882→			}
   883→		}
   884→	}
   885→	return false
   886→}
   887→
   888→func (g *Generator) exprNeedsMath(expr ast.Expression) bool {
   889→	if expr == nil {
   890→		return false
   891→	}
   892→	switch e := expr.(type) {
   893→	case *ast.MostExpr:
   894→		// BYTE uses literal 0/255, doesn't need math
   895→		return e.ExprType != "BYTE"
   896→	case *ast.BinaryExpr:
   897→		return g.exprNeedsMath(e.Left) || g.exprNeedsMath(e.Right)
   898→	case *ast.UnaryExpr:
   899→		return g.exprNeedsMath(e.Right)
   900→	case *ast.ParenExpr:
   901→		return g.exprNeedsMath(e.Expr)
   902→	case *ast.TypeConversion:
   903→		return g.exprNeedsMath(e.Expr)
   904→	case *ast.SizeExpr:
   905→		return g.exprNeedsMath(e.Expr)
   906→	case *ast.IndexExpr:
   907→		return g.exprNeedsMath(e.Left) || g.exprNeedsMath(e.Index)
   908→	case *ast.FuncCall:
   909→		for _, arg := range e.Args {
   910→			if g.exprNeedsMath(arg) {
   911→				return true
   912→			}
   913→		}
   914→	case *ast.SliceExpr:
   915→		return g.exprNeedsMath(e.Array) || g.exprNeedsMath(e.Start) || g.exprNeedsMath(e.Length)
   916→	case *ast.ArrayLiteral:
   917→		for _, elem := range e.Elements {
   918→			if g.exprNeedsMath(elem) {
   919→				return true
   920→			}
   921→		}
   922→	}
   923→	return false
   924→}
   925→
   926→func (g *Generator) generateMostExpr(e *ast.MostExpr) {
   927→	switch e.ExprType {
   928→	case "INT":
   929→		if e.IsNeg {
   930→			g.write("math.MinInt")
   931→		} else {
   932→			g.write("math.MaxInt")
   933→		}
   934→	case "BYTE":
   935→		if e.IsNeg {
   936→			g.write("0")
   937→		} else {
   938→			g.write("255")
   939→		}
   940→	case "REAL32":
   941→		if e.IsNeg {
   942→			g.write("-math.MaxFloat32")
   943→		} else {
   944→			g.write("math.MaxFloat32")
   945→		}
   946→	case "REAL64":
   947→		if e.IsNeg {
   948→			g.write("-math.MaxFloat64")
   949→		} else {
   950→			g.write("math.MaxFloat64")
   951→		}
   952→	}
   953→}
   954→
   955→func (g *Generator) writeLine(s string) {
   956→	if s == "" {
   957→		g.builder.WriteString("\n")
   958→		return
   959→	}
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/codegen/codegen.go",
  "offset": 1380,
  "limit": 80
}
  1380→		for _, c := range s.Cases {
  1381→			for _, inner := range c.Body {
  1382→				g.collectRecordVars(inner)
  1383→			}
  1384→		}
  1385→	}
  1386→}
  1387→
  1388→func (g *Generator) generateRecordDecl(rec *ast.RecordDecl) {
  1389→	g.writeLine(fmt.Sprintf("type %s struct {", goIdent(rec.Name)))
  1390→	g.indent++
  1391→	for _, f := range rec.Fields {
  1392→		goType := g.occamTypeToGoBase(f.Type)
  1393→		g.writeLine(fmt.Sprintf("%s %s", goIdent(f.Name), goType))
  1394→	}
  1395→	g.indent--
  1396→	g.writeLine("}")
  1397→	g.writeLine("")
  1398→}
  1399→
  1400→// occamTypeToGoBase converts a type name without checking protocol defs
  1401→// (used inside protocol generation to avoid infinite recursion)
  1402→func (g *Generator) occamTypeToGoBase(occamType string) string {
  1403→	switch occamType {
  1404→	case "INT":
  1405→		return "int"
  1406→	case "BYTE":
  1407→		return "byte"
  1408→	case "BOOL":
  1409→		return "bool"
  1410→	case "REAL":
  1411→		return "float64"
  1412→	case "REAL32":
  1413→		return "float32"
  1414→	case "REAL64":
  1415→		return "float64"
  1416→	default:
  1417→		return occamType
  1418→	}
  1419→}
  1420→
  1421→func (g *Generator) occamTypeToGo(occamType string) string {
  1422→	switch occamType {
  1423→	case "INT":
  1424→		return "int"
  1425→	case "BYTE":
  1426→		return "byte"
  1427→	case "BOOL":
  1428→		return "bool"
  1429→	case "REAL":
  1430→		return "float64"
  1431→	case "REAL32":
  1432→		return "float32"
  1433→	case "REAL64":
  1434→		return "float64"
  1435→	default:
  1436→		// Check if it's a protocol name
  1437→		if _, ok := g.protocolDefs[occamType]; ok {
  1438→			return "_proto_" + occamType
  1439→		}
  1440→		// Check if it's a record type name
  1441→		if _, ok := g.recordDefs[occamType]; ok {
  1442→			return occamType
  1443→		}
  1444→		return occamType // pass through unknown types
  1445→	}
  1446→}
  1447→
  1448→func (g *Generator) generateAssignment(assign *ast.Assignment) {
  1449→	g.builder.WriteString(strings.Repeat("\t", g.indent))
  1450→
  1451→	if assign.SliceTarget != nil {
  1452→		// Slice assignment: [arr FROM start FOR length] := value
  1453→		// Maps to: copy(arr[start : start + length], value)
  1454→		g.write("copy(")
  1455→		g.generateExpression(assign.SliceTarget.Array)
  1456→		g.write("[")
  1457→		g.generateExpression(assign.SliceTarget.Start)
  1458→		g.write(" : ")
  1459→		g.generateExpression(assign.SliceTarget.Start)
Grep
{
  "pattern": "func isTypeToken",
  "path": "/home/david/projects/code-associates/occam2go/parser/parser.go",
  "output_mode": "content",
  "-A": 5
}
1979:func isTypeToken(t lexer.TokenType) bool {
1980-	return t == lexer.INT_TYPE || t == lexer.BYTE_TYPE ||
1981-		t == lexer.BOOL_TYPE || t == lexer.REAL_TYPE ||
1982-		t == lexer.REAL32_TYPE || t == lexer.REAL64_TYPE
1983-}
1984-
Grep
{
  "pattern": "MOSTNEG|MOSTPOS",
  "path": "/home/david/projects/code-associates/occam2go/parser/parser.go",
  "output_mode": "content",
  "-B": 2,
  "-A": 10
}
2789-			Expr:  p.parseExpression(PREFIX),
2790-		}
2791:	case lexer.MOSTNEG_KW, lexer.MOSTPOS_KW:
2792-		token := p.curToken
2793:		isNeg := token.Type == lexer.MOSTNEG_KW
2794-		// Expect a type name next
2795-		if !p.peekTokenIs(lexer.INT_TYPE) && !p.peekTokenIs(lexer.BYTE_TYPE) &&
2796-			!p.peekTokenIs(lexer.BOOL_TYPE) && !p.peekTokenIs(lexer.REAL_TYPE) &&
2797-			!p.peekTokenIs(lexer.REAL32_TYPE) && !p.peekTokenIs(lexer.REAL64_TYPE) {
2798-			p.addError(fmt.Sprintf("expected type after %s, got %s", token.Literal, p.peekToken.Type))
2799-			return nil
2800-		}
2801-		p.nextToken()
2802-		left = &ast.MostExpr{
2803-			Token:    token,
Thinking

Now let me also read the type conversion parsing area and the TODO.md file.

Now let me read the type conversion prefix area and TODO.md.

Grep
{
  "pattern": "case lexer\\.INT_TYPE, lexer\\.BYTE_TYPE.*lexer\\.REAL_TYPE",
  "path": "/home/david/projects/code-associates/occam2go/parser/parser.go",
  "output_mode": "content",
  "-B": 2,
  "-A": 15
}
166-	case lexer.INITIAL:
167-		return p.parseInitialDecl()
168:	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE:
169-		if p.peekTokenIs(lexer.FUNCTION) || p.peekTokenIs(lexer.FUNC) || p.peekTokenIs(lexer.COMMA) || p.peekTokenIs(lexer.INLINE) {
170-			return p.parseFuncDecl()
171-		}
172-		return p.parseVarDeclOrAbbreviation()
173-	case lexer.LBRACKET:
174-		return p.parseArrayDecl()
175-	case lexer.CHAN:
176-		return p.parseChanDecl()
177-	case lexer.PROTOCOL:
178-		return p.parseProtocolDecl()
179-	case lexer.RECORD:
180-		return p.parseRecordDecl()
181-	case lexer.TIMER:
182-		return p.parseTimerDecl()
183-	case lexer.SEQ:
--
1651-func (p *Parser) isAltDeclStart() bool {
1652-	switch p.curToken.Type {
1653:	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE:
1654-		return true
1655-	case lexer.VAL:
1656-		return true
1657-	case lexer.INITIAL:
1658-		return true
1659-	}
1660-	return false
1661-}
1662-
1663-func (p *Parser) parseAltCase() *ast.AltCase {
1664-	altCase := &ast.AltCase{}
1665-
1666-	/ Parse scoped declarations before the channel input (e.g., BYTE ch:, VAL INT X IS expr:)
1667-	for p.isAltDeclStart() {
1668-		stmt := p.parseStatement()
--
2805-			IsNeg:    isNeg,
2806-		}
2807:	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE:
2808-		token := p.curToken
2809-		p.nextToken()
2810-		left = &ast.TypeConversion{
2811-			Token:      token,
2812-			TargetType: token.Literal,
2813-			Expr:       p.parseExpression(PREFIX),
2814-		}
2815-	default:
2816-		p.addError(fmt.Sprintf("unexpected token in expression: %s", p.curToken.Type))
2817-		return nil
2818-	}
2819-
2820-	// Parse infix expressions
2821-	for !p.peekTokenIs(lexer.NEWLINE) && !p.peekTokenIs(lexer.EOF) &&
2822-		precedence < p.peekPrecedence() {
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/TODO.md"
}
     1→# occam2go — Implementation Status
     2→
     3→## Fully Implemented
     4→
     5→### Core Constructs
     6→- **SEQ** — Sequential execution, with replicators (`SEQ i = 0 FOR n`) and optional STEP
     7→- **PAR** — Parallel execution via goroutines + sync.WaitGroup, with replicators
     8→- **IF** — Multi-branch conditionals, maps to if/else if chains, with replicators; supports multi-statement bodies (declarations scoped before process)
     9→- **WHILE** — Loops, maps to Go `for` loops; supports multi-statement bodies
    10→- **CASE** — Pattern matching with multiple cases and ELSE branch; supports multi-statement bodies
    11→- **ALT** — Channel alternation, maps to Go `select`; supports boolean guards, timer timeouts, multi-statement bodies, and replicators (`ALT i = 0 FOR n` using `reflect.Select`)
    12→- **SKIP** — No-op process
    13→- **STOP** — Error + deadlock
    14→
    15→### Data Types & Declarations
    16→- **INT, BYTE, BOOL, REAL, REAL32, REAL64** — Scalar types (REAL/REAL64 map to float64, REAL32 maps to float32)
    17→- **Variable declarations** — `INT x, y, z:`
    18→- **Arrays** — `[n]TYPE arr:` with index expressions
    19→- **Channels** — `CHAN OF TYPE c:` with send (`!`) and receive (`?`); `CHAN BYTE` shorthand (without `OF`)
    20→- **Channel arrays** — `[n]CHAN OF TYPE cs:` with indexed send/receive and `[]CHAN OF TYPE` proc params
    21→- **Channel direction** — `CHAN OF INT c?` (receive-only) and `CHAN OF INT c!` (send-only); direction annotations at call sites (`out!`, `in?`) accepted and ignored
    22→- **Timers** — `TIMER tim:` with reads and `AFTER` expressions
    23→- **Abbreviations** — `VAL INT x IS 1:`, `INT y IS z:`, untyped `VAL x IS expr:` — named constants and aliases
    24→- **INITIAL declarations** — `INITIAL INT x IS 42:` — mutable variables with initial values
    25→- **Byte literals** — `'A'`, `'0'` with occam escape sequences (`*n`, `*c`, `*t`)
    26→- **Hex integer literals** — `#FF`, `#80000000`
    27→
    28→### Procedures & Functions
    29→- **PROC** — Declaration with VAL, reference, CHAN OF, and open array (`[]TYPE`) parameters
    30→- **PROC calls** — With automatic `&`/`*` for reference params, pass-through for channels
    31→- **FUNCTION (IS form)** — `INT FUNCTION square(VAL INT x) IS x * x`
    32→- **FUNCTION (VALOF form)** — Local declarations + VALOF body + RESULT
    33→- **Multi-result FUNCTIONs** — `INT, INT FUNCTION f(...)` returning multiple values via `RESULT a, b`
    34→- **Nested PROCs/FUNCTIONs** — Local definitions inside a PROC body, compiled as Go closures
    35→- **KRoC-style colon terminators** — Optional `:` at end of PROC/FUNCTION body
    36→- **INLINE modifier** — `INT INLINE FUNCTION f(...)` — accepted and ignored (optimization hint only)
    37→- **Built-in print** — `print.int`, `print.bool`, `print.string`, `print.newline`
    38→
    39→### Expressions & Operators
    40→- **Arithmetic** — `+`, `-`, `*`, `/`, `\` (modulo)
    41→- **Comparison** — `=`, `<>`, `<`, `>`, `<=`, `>=`
    42→- **Logical** — `AND`, `OR`, `NOT`
    43→- **Bitwise** — `/\`, `\/`, `><`, `~`, `<<`, `>>`
    44→- **AFTER** — As boolean expression (maps to `>`)
    45→- **Parenthesized expressions**
    46→- **Array indexing** — `arr[i]`, `arr[expr]`
    47→- **String literals** — Double-quoted strings
    48→- **Type conversions** — `INT expr`, `BYTE expr`, `REAL32 expr`, `REAL64 expr`
    49→- **Checked arithmetic** — `PLUS`, `MINUS`, `TIMES` — modular (wrapping) operators
    50→- **MOSTNEG/MOSTPOS** — Type min/max constants for INT, BYTE, REAL32, REAL64
    51→- **SIZE operator** — `SIZE arr`, `SIZE "str"` maps to `len()`
    52→- **Array slices** — `[arr FROM n FOR m]` with slice assignment
    53→- **Array literals** — `[1, 2, 3]` — inline array/table expressions
    54→- **Multi-assignment** — `a, b := f(...)` including indexed targets like `x[0], x[1] := x[1], x[0]`
    55→- **Multi-line expression continuation** — Binary operators and `:=` at end of line continue expression on next line
    56→
    57→### Protocols
    58→- **Simple** — `PROTOCOL SIG IS INT` (type alias)
    59→- **Sequential** — `PROTOCOL PAIR IS INT ; BYTE` (struct)
    60→- **Variant** — `PROTOCOL MSG CASE tag; TYPE ...` (interface + concrete types)
    61→
    62→### Records
    63→- **RECORD** — Struct types with field access via bracket syntax (`p[x]`)
    64→
    65→### Type Reinterpretation & Intrinsics
    66→- **RETYPES** — Bit-level type reinterpretation (`VAL INT X RETYPES X :` for float32→int, `VAL [2]INT X RETYPES X :` for float64→int pair)
    67→- **Transputer intrinsics** — `LONGPROD`, `LONGDIV`, `LONGSUM`, `LONGDIFF`, `NORMALISE`, `SHIFTLEFT`, `SHIFTRIGHT` — extended-precision arithmetic as Go helper functions
    68→- **CAUSEERROR** — Error-raising primitive, maps to `panic("CAUSEERROR")`
    69→
    70→### Preprocessor
    71→- **`#IF` / `#ELSE` / `#ENDIF`** — Conditional compilation with `TRUE`, `FALSE`, `DEFINED()`, `NOT`, equality
    72→- **`#DEFINE`** — Symbol definition
    73→- **`#INCLUDE`** — File inclusion with search paths and include guards
    74→- **`#COMMENT` / `#PRAGMA` / `#USE`** — Ignored (blank lines)
    75→- **Predefined symbols** — `TARGET.BITS.PER.WORD = 64`
    76→
    77→### Tooling
    78→- **gen-module** — Generate `.module` files from KRoC SConscript build files
    79→
    80→---
    81→
    82→## Not Yet Implemented
    83→
    84→### Required for shared_screen module (extends course module)
    85→
    86→| Feature | Notes | Used in |
    87→|---------|-------|---------|
    88→| **`DATA TYPE X IS TYPE:`** | Simple type alias (e.g. `DATA TYPE COLOUR IS BYTE:`). | shared_screen.inc |
    89→| **`DATA TYPE X RECORD`** | Alternative record syntax (vs current `RECORD X`). | shared_screen.inc |
    90→| **Counted array protocol** | `BYTE::[]BYTE` — length-prefixed array in protocols. | shared_screen.inc, shared_screen.occ |
    91→| **`RESULT` param qualifier** | `RESULT INT len` on PROC params (output-only, like a write-only reference). | float_io.occ |
    92→
    93→### Other language features
    94→
    95→| Feature | Notes |
    96→|---------|-------|
    97→| **PRI ALT / PRI PAR** | Priority variants of ALT and PAR. |
    98→| **PLACED PAR** | Assigning processes to specific hardware. |
    99→| **PORT OF** | Hardware port mapping. |
   100→| **`VAL []BYTE` abbreviations** | `VAL []BYTE cmap IS "0123456789ABCDEF":` — named string constants. |
   101→| **`#PRAGMA DEFINED`** | Compiler hint to suppress definedness warnings. Can be ignored. |
   102→
Thinking

Now I have a clear picture of all the changes needed. Let me implement them all.

I have all the context I need. Let me implement the changes across all files.

TaskCreate
Add support for INT16, INT32, INT64 across lexer, parser, codegen, tests, and docs
{
  "subject": "Implement INT16, INT32, INT64 integer types",
  "activeForm": "Implementing INT16/INT32/INT64 types"
}
Task #1 created successfully: Implement INT16, INT32, INT64 integer types
TaskUpdate
{
  "taskId": "1",
  "status": "in_progress"
}
Updated task #1 status

1. Lexer — Add token types and keywords

✏️ Edit token.go
/home/david/projects/code-associates/occam2go/lexer/token.go
	REAL32_TYPE
	REAL64_TYPE
+
	REAL32_TYPE
	REAL64_TYPE
	INT16_TYPE
	INT32_TYPE
	INT64_TYPE
The file /home/david/projects/code-associates/occam2go/lexer/token.go has been updated successfully.
✏️ Edit token.go
/home/david/projects/code-associates/occam2go/lexer/token.go
	REAL32_TYPE: "REAL32",
	REAL64_TYPE: "REAL64",
+
	REAL32_TYPE: "REAL32",
	REAL64_TYPE: "REAL64",
	INT16_TYPE:  "INT16",
	INT32_TYPE:  "INT32",
	INT64_TYPE:  "INT64",
The file /home/david/projects/code-associates/occam2go/lexer/token.go has been updated successfully.
✏️ Edit token.go
/home/david/projects/code-associates/occam2go/lexer/token.go
	"REAL32": REAL32_TYPE,
	"REAL64": REAL64_TYPE,
+
	"REAL32": REAL32_TYPE,
	"REAL64": REAL64_TYPE,
	"INT16":  INT16_TYPE,
	"INT32":  INT32_TYPE,
	"INT64":  INT64_TYPE,
The file /home/david/projects/code-associates/occam2go/lexer/token.go has been updated successfully.

2. Parser — Update isTypeToken, MOSTNEG/MOSTPOS guard, and type conversion prefix

✏️ Edit parser.go
/home/david/projects/code-associates/occam2go/parser/parser.go
func isTypeToken(t lexer.TokenType) bool {
	return t == lexer.INT_TYPE || t == lexer.BYTE_TYPE ||
		t == lexer.BOOL_TYPE || t == lexer.REAL_TYPE ||
		t == lexer.REAL32_TYPE || t == lexer.REAL64_TYPE
}
+
func isTypeToken(t lexer.TokenType) bool {
	return t == lexer.INT_TYPE || t == lexer.BYTE_TYPE ||
		t == lexer.BOOL_TYPE || t == lexer.REAL_TYPE ||
		t == lexer.REAL32_TYPE || t == lexer.REAL64_TYPE ||
		t == lexer.INT16_TYPE || t == lexer.INT32_TYPE || t == lexer.INT64_TYPE
}
<tool_use_error>File has not been read yet. Read it first before writing to it.</tool_use_error>
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/parser/parser.go",
  "offset": 1975,
  "limit": 10
}
  1975→	return proc
  1976→}
  1977→
  1978→// isTypeToken returns true if the token type is a scalar type keyword.
  1979→func isTypeToken(t lexer.TokenType) bool {
  1980→	return t == lexer.INT_TYPE || t == lexer.BYTE_TYPE ||
  1981→		t == lexer.BOOL_TYPE || t == lexer.REAL_TYPE ||
  1982→		t == lexer.REAL32_TYPE || t == lexer.REAL64_TYPE
  1983→}
  1984→
✏️ Edit parser.go
/home/david/projects/code-associates/occam2go/parser/parser.go
func isTypeToken(t lexer.TokenType) bool {
	return t == lexer.INT_TYPE || t == lexer.BYTE_TYPE ||
		t == lexer.BOOL_TYPE || t == lexer.REAL_TYPE ||
		t == lexer.REAL32_TYPE || t == lexer.REAL64_TYPE
}
+
func isTypeToken(t lexer.TokenType) bool {
	return t == lexer.INT_TYPE || t == lexer.BYTE_TYPE ||
		t == lexer.BOOL_TYPE || t == lexer.REAL_TYPE ||
		t == lexer.REAL32_TYPE || t == lexer.REAL64_TYPE ||
		t == lexer.INT16_TYPE || t == lexer.INT32_TYPE || t == lexer.INT64_TYPE
}
The file /home/david/projects/code-associates/occam2go/parser/parser.go has been updated successfully.

Now update the MOSTNEG/MOSTPOS guard and type conversion prefix. Let me read those exact lines.

Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/parser/parser.go",
  "offset": 2791,
  "limit": 30
}
  2791→		}
  2792→	case lexer.MOSTNEG_KW, lexer.MOSTPOS_KW:
  2793→		token := p.curToken
  2794→		isNeg := token.Type == lexer.MOSTNEG_KW
  2795→		// Expect a type name next
  2796→		if !p.peekTokenIs(lexer.INT_TYPE) && !p.peekTokenIs(lexer.BYTE_TYPE) &&
  2797→			!p.peekTokenIs(lexer.BOOL_TYPE) && !p.peekTokenIs(lexer.REAL_TYPE) &&
  2798→			!p.peekTokenIs(lexer.REAL32_TYPE) && !p.peekTokenIs(lexer.REAL64_TYPE) {
  2799→			p.addError(fmt.Sprintf("expected type after %s, got %s", token.Literal, p.peekToken.Type))
  2800→			return nil
  2801→		}
  2802→		p.nextToken()
  2803→		left = &ast.MostExpr{
  2804→			Token:    token,
  2805→			ExprType: p.curToken.Literal,
  2806→			IsNeg:    isNeg,
  2807→		}
  2808→	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE:
  2809→		token := p.curToken
  2810→		p.nextToken()
  2811→		left = &ast.TypeConversion{
  2812→			Token:      token,
  2813→			TargetType: token.Literal,
  2814→			Expr:       p.parseExpression(PREFIX),
  2815→		}
  2816→	default:
  2817→		p.addError(fmt.Sprintf("unexpected token in expression: %s", p.curToken.Type))
  2818→		return nil
  2819→	}
  2820→
✏️ Edit parser.go
/home/david/projects/code-associates/occam2go/parser/parser.go
		if !p.peekTokenIs(lexer.INT_TYPE) && !p.peekTokenIs(lexer.BYTE_TYPE) &&
			!p.peekTokenIs(lexer.BOOL_TYPE) && !p.peekTokenIs(lexer.REAL_TYPE) &&
			!p.peekTokenIs(lexer.REAL32_TYPE) && !p.peekTokenIs(lexer.REAL64_TYPE) {
+
		if !p.peekTokenIs(lexer.INT_TYPE) && !p.peekTokenIs(lexer.BYTE_TYPE) &&
			!p.peekTokenIs(lexer.BOOL_TYPE) && !p.peekTokenIs(lexer.REAL_TYPE) &&
			!p.peekTokenIs(lexer.REAL32_TYPE) && !p.peekTokenIs(lexer.REAL64_TYPE) &&
			!p.peekTokenIs(lexer.INT16_TYPE) && !p.peekTokenIs(lexer.INT32_TYPE) && !p.peekTokenIs(lexer.INT64_TYPE) {
The file /home/david/projects/code-associates/occam2go/parser/parser.go has been updated successfully.
✏️ Edit parser.go
/home/david/projects/code-associates/occam2go/parser/parser.go
	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE:
		token := p.curToken
		p.nextToken()
		left = &ast.TypeConversion{
+
	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE,
		lexer.INT16_TYPE, lexer.INT32_TYPE, lexer.INT64_TYPE:
		token := p.curToken
		p.nextToken()
		left = &ast.TypeConversion{
The file /home/david/projects/code-associates/occam2go/parser/parser.go has been updated successfully.

Now update the two other case lists that use INT_TYPE directly (the parseStatement switch and isAltDeclStart).

Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/parser/parser.go",
  "offset": 165,
  "limit": 10
}
   165→		return p.parseAbbreviation()
   166→	case lexer.INITIAL:
   167→		return p.parseInitialDecl()
   168→	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE:
   169→		if p.peekTokenIs(lexer.FUNCTION) || p.peekTokenIs(lexer.FUNC) || p.peekTokenIs(lexer.COMMA) || p.peekTokenIs(lexer.INLINE) {
   170→			return p.parseFuncDecl()
   171→		}
   172→		return p.parseVarDeclOrAbbreviation()
   173→	case lexer.LBRACKET:
   174→		return p.parseArrayDecl()
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/parser/parser.go",
  "offset": 1649,
  "limit": 15
}
  1649→}
  1650→
  1651→func (p *Parser) isAltDeclStart() bool {
  1652→	switch p.curToken.Type {
  1653→	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE:
  1654→		return true
  1655→	case lexer.VAL:
  1656→		return true
  1657→	case lexer.INITIAL:
  1658→		return true
  1659→	}
  1660→	return false
  1661→}
  1662→
  1663→func (p *Parser) parseAltCase() *ast.AltCase {
✏️ Edit parser.go
/home/david/projects/code-associates/occam2go/parser/parser.go
	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE:
		if p.peekTokenIs(lexer.FUNCTION) || p.peekTokenIs(lexer.FUNC) || p.peekTokenIs(lexer.COMMA) || p.peekTokenIs(lexer.INLINE) {
			return p.parseFuncDecl()
		}
		return p.parseVarDeclOrAbbreviation()
+
	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE,
		lexer.INT16_TYPE, lexer.INT32_TYPE, lexer.INT64_TYPE:
		if p.peekTokenIs(lexer.FUNCTION) || p.peekTokenIs(lexer.FUNC) || p.peekTokenIs(lexer.COMMA) || p.peekTokenIs(lexer.INLINE) {
			return p.parseFuncDecl()
		}
		return p.parseVarDeclOrAbbreviation()
The file /home/david/projects/code-associates/occam2go/parser/parser.go has been updated successfully.
✏️ Edit parser.go
/home/david/projects/code-associates/occam2go/parser/parser.go
	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE:
		return true
	case lexer.VAL:
+
	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE,
		lexer.INT16_TYPE, lexer.INT32_TYPE, lexer.INT64_TYPE:
		return true
	case lexer.VAL:
The file /home/david/projects/code-associates/occam2go/parser/parser.go has been updated successfully.

3. Codegen — Add type mappings and MOSTNEG/MOSTPOS cases

✏️ Edit codegen.go
/home/david/projects/code-associates/occam2go/codegen/codegen.go
func (g *Generator) occamTypeToGoBase(occamType string) string {
	switch occamType {
	case "INT":
		return "int"
	case "BYTE":
		return "byte"
	case "BOOL":
		return "bool"
	case "REAL":
		return "float64"
	case "REAL32":
		return "float32"
	case "REAL64":
		return "float64"
	default:
		return occamType
	}
}
+
func (g *Generator) occamTypeToGoBase(occamType string) string {
	switch occamType {
	case "INT":
		return "int"
	case "INT16":
		return "int16"
	case "INT32":
		return "int32"
	case "INT64":
		return "int64"
	case "BYTE":
		return "byte"
	case "BOOL":
		return "bool"
	case "REAL":
		return "float64"
	case "REAL32":
		return "float32"
	case "REAL64":
		return "float64"
	default:
		return occamType
	}
}
The file /home/david/projects/code-associates/occam2go/codegen/codegen.go has been updated successfully.
✏️ Edit codegen.go
/home/david/projects/code-associates/occam2go/codegen/codegen.go
func (g *Generator) occamTypeToGo(occamType string) string {
	switch occamType {
	case "INT":
		return "int"
	case "BYTE":
		return "byte"
	case "BOOL":
		return "bool"
	case "REAL":
		return "float64"
	case "REAL32":
		return "float32"
	case "REAL64":
		return "float64"
+
func (g *Generator) occamTypeToGo(occamType string) string {
	switch occamType {
	case "INT":
		return "int"
	case "INT16":
		return "int16"
	case "INT32":
		return "int32"
	case "INT64":
		return "int64"
	case "BYTE":
		return "byte"
	case "BOOL":
		return "bool"
	case "REAL":
		return "float64"
	case "REAL32":
		return "float32"
	case "REAL64":
		return "float64"
The file /home/david/projects/code-associates/occam2go/codegen/codegen.go has been updated successfully.

Now add MOSTNEG/MOSTPOS cases for INT16/32/64.

✏️ Edit codegen.go
/home/david/projects/code-associates/occam2go/codegen/codegen.go
func (g *Generator) generateMostExpr(e *ast.MostExpr) {
	switch e.ExprType {
	case "INT":
		if e.IsNeg {
			g.write("math.MinInt")
		} else {
			g.write("math.MaxInt")
		}
	case "BYTE":
		if e.IsNeg {
			g.write("0")
		} else {
			g.write("255")
		}
	case "REAL32":
		if e.IsNeg {
			g.write("-math.MaxFloat32")
		} else {
			g.write("math.MaxFloat32")
		}
	case "REAL64":
		if e.IsNeg {
			g.write("-math.MaxFloat64")
		} else {
			g.write("math.MaxFloat64")
		}
	}
}
+
func (g *Generator) generateMostExpr(e *ast.MostExpr) {
	switch e.ExprType {
	case "INT":
		if e.IsNeg {
			g.write("math.MinInt")
		} else {
			g.write("math.MaxInt")
		}
	case "INT16":
		if e.IsNeg {
			g.write("math.MinInt16")
		} else {
			g.write("math.MaxInt16")
		}
	case "INT32":
		if e.IsNeg {
			g.write("math.MinInt32")
		} else {
			g.write("math.MaxInt32")
		}
	case "INT64":
		if e.IsNeg {
			g.write("math.MinInt64")
		} else {
			g.write("math.MaxInt64")
		}
	case "BYTE":
		if e.IsNeg {
			g.write("0")
		} else {
			g.write("255")
		}
	case "REAL32":
		if e.IsNeg {
			g.write("-math.MaxFloat32")
		} else {
			g.write("math.MaxFloat32")
		}
	case "REAL64":
		if e.IsNeg {
			g.write("-math.MaxFloat64")
		} else {
			g.write("math.MaxFloat64")
		}
	}
}
The file /home/david/projects/code-associates/occam2go/codegen/codegen.go has been updated successfully.

4. Tests — Add parser, codegen, and e2e tests

Let me find the test files to see the patterns used.

Grep
{
  "pattern": "REAL32.*decl|REAL64.*decl|func Test.*VarDecl|func Test.*Real",
  "path": "/home/david/projects/code-associates/occam2go/parser/parser_test.go",
  "output_mode": "content",
  "-A": 2
}
10:func TestVarDecl(t *testing.T) {
11-	input := `INT x:
12-`
--
36:func TestMultipleVarDecl(t *testing.T) {
37-	input := `INT x, y, z:
38-`
--
1781:func TestRecordVarDecl(t *testing.T) {
1782-	input := `RECORD POINT
1783-  INT x:
--
2776:func TestProcLocalVarDecls(t *testing.T) {
2777-	input := `PROC foo(VAL INT n)
2778-  INT x:
Grep
{
  "pattern": "func Test.*TypeConv|func Test.*MOST",
  "path": "/home/david/projects/code-associates/occam2go/codegen/codegen_test.go",
  "output_mode": "content",
  "-A": 2
}
297:func TestTypeConversion(t *testing.T) {
298-	tests := []struct {
299-		input    string
Grep
{
  "pattern": "func TestE2E.*Type|func TestE2E.*Most|func TestE2E.*Real",
  "path": "/home/david/projects/code-associates/occam2go",
  "output_mode": "content",
  "-A": 2
}
codegen/e2e_params_test.go:69:func TestE2E_SharedTypeChanParams(t *testing.T) {
/home/david/projects/code-associates/occam2go/codegen/e2e_params_test.go-70-	// PROC f(CHAN OF INT a?, b?) — type applies to both a and b
codegen/e2e_params_test.go-71-	occam := `PROC relay(CHAN OF INT input?, output!)
--
codegen/e2e_params_test.go:96:func TestE2E_SharedTypeIntParams(t *testing.T) {
/home/david/projects/code-associates/occam2go/codegen/e2e_params_test.go-97-	// PROC f(VAL INT a, b) — type applies to both a and b
codegen/e2e_params_test.go-98-	occam := `PROC add(VAL INT a, b, INT result)
--
codegen/e2e_types_test.go:5:func TestE2E_TypeConversionIntFromByte(t *testing.T) {
codegen/e2e_types_test.go-6-	occam := `SEQ
codegen/e2e_types_test.go-7-  BYTE b:
--
codegen/e2e_types_test.go:20:func TestE2E_TypeConversionByteFromInt(t *testing.T) {
codegen/e2e_types_test.go-21-	occam := `SEQ
codegen/e2e_types_test.go-22-  INT n:
--
codegen/e2e_types_test.go:35:func TestE2E_TypeConversionInExpression(t *testing.T) {
codegen/e2e_types_test.go-36-	occam := `SEQ
codegen/e2e_types_test.go-37-  BYTE b:
--
codegen/e2e_types_test.go:50:func TestE2E_Real32VarDecl(t *testing.T) {
codegen/e2e_types_test.go-51-	occam := `SEQ
codegen/e2e_types_test.go-52-  REAL32 x:
--
codegen/e2e_types_test.go:63:func TestE2E_Real64VarDecl(t *testing.T) {
codegen/e2e_types_test.go-64-	occam := `SEQ
codegen/e2e_types_test.go-65-  REAL64 x:
--
codegen/e2e_types_test.go:76:func TestE2E_Real32ToReal64Conversion(t *testing.T) {
codegen/e2e_types_test.go-77-	occam := `SEQ
codegen/e2e_types_test.go-78-  REAL32 a:
--
codegen/e2e_types_test.go:91:func TestE2E_Real32Array(t *testing.T) {
codegen/e2e_types_test.go-92-	occam := `SEQ
codegen/e2e_types_test.go-93-  [3]REAL32 arr:
--
codegen/e2e_types_test.go:257:func TestE2E_MostNegInt(t *testing.T) {
codegen/e2e_types_test.go-258-	occam := `SEQ
codegen/e2e_types_test.go-259-  INT x:
--
codegen/e2e_types_test.go:276:func TestE2E_MostPosInt(t *testing.T) {
codegen/e2e_types_test.go-277-	occam := `SEQ
codegen/e2e_types_test.go-278-  INT x:
--
codegen/e2e_types_test.go:295:func TestE2E_MostNegByte(t *testing.T) {
codegen/e2e_types_test.go-296-	occam := `SEQ
codegen/e2e_types_test.go-297-  BYTE x:
--
codegen/e2e_types_test.go:308:func TestE2E_MostPosByte(t *testing.T) {
codegen/e2e_types_test.go-309-	occam := `SEQ
codegen/e2e_types_test.go-310-  BYTE x:
--
codegen/e2e_types_test.go:321:func TestE2E_MostNegInExpression(t *testing.T) {
/home/david/projects/code-associates/occam2go/codegen/e2e_types_test.go-322-	// Test MOSTNEG INT used in comparison (like utils.occ does)
codegen/e2e_types_test.go-323-	occam := `SEQ
--
codegen/e2e_misc_test.go:188:func TestE2E_MostNegReal32(t *testing.T) {
/home/david/projects/code-associates/occam2go/codegen/e2e_misc_test.go-189-	// MOSTNEG REAL32 → -math.MaxFloat32 (a very large negative number)
codegen/e2e_misc_test.go-190-	occam := `SEQ
--
codegen/e2e_misc_test.go:206:func TestE2E_MostPosReal32(t *testing.T) {
/home/david/projects/code-associates/occam2go/codegen/e2e_misc_test.go-207-	// MOSTPOS REAL32 → math.MaxFloat32
codegen/e2e_misc_test.go-208-	occam := `SEQ
--
codegen/e2e_misc_test.go:224:func TestE2E_MostNegReal64(t *testing.T) {
/home/david/projects/code-associates/occam2go/codegen/e2e_misc_test.go-225-	// MOSTNEG REAL64 → -math.MaxFloat64
codegen/e2e_misc_test.go-226-	occam := `SEQ
--
codegen/e2e_misc_test.go:242:func TestE2E_MostPosReal64(t *testing.T) {
/home/david/projects/code-associates/occam2go/codegen/e2e_misc_test.go-243-	// MOSTPOS REAL64 → math.MaxFloat64
codegen/e2e_misc_test.go-244-	occam := `SEQ

Let me look at existing test patterns to follow the same style.

Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/codegen/e2e_types_test.go",
  "offset": 1,
  "limit": 30
}
     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 {
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/codegen/codegen_test.go",
  "offset": 297,
  "limit": 60
}
   297→func TestTypeConversion(t *testing.T) {
   298→	tests := []struct {
   299→		input    string
   300→		expected string
   301→	}{
   302→		{"x := INT y\n", "x = int(y)"},
   303→		{"x := BYTE n\n", "x = byte(n)"},
   304→		{"x := REAL count\n", "x = float64(count)"},
   305→		{"x := BOOL flag\n", "x = bool(flag)"},
   306→		{"x := REAL32 y\n", "x = float32(y)"},
   307→		{"x := REAL64 y\n", "x = float64(y)"},
   308→	}
   309→
   310→	for _, tt := range tests {
   311→		output := transpile(t, tt.input)
   312→		if !strings.Contains(output, tt.expected) {
   313→			t.Errorf("for input %q: expected %q in output, got:\n%s", tt.input, tt.expected, output)
   314→		}
   315→	}
   316→}
   317→
   318→func TestMostNegMostPos(t *testing.T) {
   319→	tests := []struct {
   320→		input    string
   321→		expected string
   322→	}{
   323→		{"x := MOSTNEG INT\n", "x = math.MinInt"},
   324→		{"x := MOSTPOS INT\n", "x = math.MaxInt"},
   325→		{"x := MOSTNEG BYTE\n", "x = 0"},
   326→		{"x := MOSTPOS BYTE\n", "x = 255"},
   327→		{"x := MOSTNEG REAL32\n", "x = -math.MaxFloat32"},
   328→		{"x := MOSTPOS REAL32\n", "x = math.MaxFloat32"},
   329→		{"x := MOSTNEG REAL64\n", "x = -math.MaxFloat64"},
   330→		{"x := MOSTPOS REAL64\n", "x = math.MaxFloat64"},
   331→	}
   332→
   333→	for _, tt := range tests {
   334→		output := transpile(t, tt.input)
   335→		if !strings.Contains(output, tt.expected) {
   336→			t.Errorf("for input %q: expected %q in output, got:\n%s", tt.input, tt.expected, output)
   337→		}
   338→	}
   339→}
   340→
   341→func TestMostNegImportsMath(t *testing.T) {
   342→	output := transpile(t, "x := MOSTNEG INT\n")
   343→	if !strings.Contains(output, `"math"`) {
   344→		t.Errorf("expected math import in output, got:\n%s", output)
   345→	}
   346→}
   347→
   348→func TestMostNegByteNoMathImport(t *testing.T) {
   349→	output := transpile(t, "x := MOSTNEG BYTE\n")
   350→	if strings.Contains(output, `"math"`) {
   351→		t.Errorf("expected no math import for MOSTNEG BYTE, got:\n%s", output)
   352→	}
   353→}
   354→
   355→func TestStringLiteralInProcCall(t *testing.T) {
   356→	input := `print.string("hello")
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/parser/parser_test.go",
  "offset": 10,
  "limit": 50
}
    10→func TestVarDecl(t *testing.T) {
    11→	input := `INT x:
    12→`
    13→	l := lexer.New(input)
    14→	p := New(l)
    15→	program := p.ParseProgram()
    16→	checkParserErrors(t, p)
    17→
    18→	if len(program.Statements) != 1 {
    19→		t.Fatalf("expected 1 statement, got %d", len(program.Statements))
    20→	}
    21→
    22→	decl, ok := program.Statements[0].(*ast.VarDecl)
    23→	if !ok {
    24→		t.Fatalf("expected VarDecl, got %T", program.Statements[0])
    25→	}
    26→
    27→	if decl.Type != "INT" {
    28→		t.Errorf("expected type INT, got %s", decl.Type)
    29→	}
    30→
    31→	if len(decl.Names) != 1 || decl.Names[0] != "x" {
    32→		t.Errorf("expected name 'x', got %v", decl.Names)
    33→	}
    34→}
    35→
    36→func TestMultipleVarDecl(t *testing.T) {
    37→	input := `INT x, y, z:
    38→`
    39→	l := lexer.New(input)
    40→	p := New(l)
    41→	program := p.ParseProgram()
    42→	checkParserErrors(t, p)
    43→
    44→	if len(program.Statements) != 1 {
    45→		t.Fatalf("expected 1 statement, got %d", len(program.Statements))
    46→	}
    47→
    48→	decl, ok := program.Statements[0].(*ast.VarDecl)
    49→	if !ok {
    50→		t.Fatalf("expected VarDecl, got %T", program.Statements[0])
    51→	}
    52→
    53→	expected := []string{"x", "y", "z"}
    54→	if len(decl.Names) != len(expected) {
    55→		t.Fatalf("expected %d names, got %d", len(expected), len(decl.Names))
    56→	}
    57→	for i, name := range expected {
    58→		if decl.Names[i] != name {
    59→			t.Errorf("expected name %s at position %d, got %s", name, i, decl.Names[i])
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/codegen/e2e_types_test.go",
  "offset": 257,
  "limit": 65
}
   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) {

Now I have enough context. Let me add the tests. First, parser test, then codegen test, then e2e test.

Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/parser/parser_test.go",
  "offset": 1,
  "limit": 9
}
     1→package parser
     2→
     3→import (
     4→	"testing"
     5→
     6→	"github.com/codeassociates/occam2go/ast"
     7→	"github.com/codeassociates/occam2go/lexer"
     8→)
     9→
Grep
{
  "pattern": "^func Test.*\\(t \\*testing\\.T\\) \\{$",
  "path": "/home/david/projects/code-associates/occam2go/parser/parser_test.go",
  "output_mode": "content",
  "head_limit": 5
}
10:func TestVarDecl(t *testing.T) {
36:func TestMultipleVarDecl(t *testing.T) {
64:func TestAssignment(t *testing.T) {
95:func TestBinaryExpression(t *testing.T) {
133:func TestSeqBlock(t *testing.T) {

[Showing results with pagination = limit: 5, offset: 0]

Let me find the end of the parser test file and the end of codegen_test.go and e2e_types_test.go.

$ Bash
Count lines in test files
wc -l /home/david/projects/code-associates/occam2go/parser/parser_test.go /home/david/projects/code-associates/occam2go/codegen/codegen_test.go /home/david/projects/code-associates/occam2go/codegen/e2e_types_test.go
  3314 /home/david/projects/code-associates/occam2go/parser/parser_test.go
   669 /home/david/projects/code-associates/occam2go/codegen/codegen_test.go
   402 /home/david/projects/code-associates/occam2go/codegen/e2e_types_test.go
  4385 total
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/parser/parser_test.go",
  "offset": 3310,
  "limit": 5
}
  3310→	}
  3311→	if c.Variable != "any" {
  3312→		t.Errorf("expected variable 'any', got %q", c.Variable)
  3313→	}
  3314→}
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/codegen/codegen_test.go",
  "offset": 296,
  "limit": 5
}
   296→
   297→func TestTypeConversion(t *testing.T) {
   298→	tests := []struct {
   299→		input    string
   300→		expected string
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/codegen/e2e_types_test.go",
  "offset": 395,
  "limit": 10
}
   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→

Now let me add all the tests.

✏️ Edit parser_test.go
/home/david/projects/code-associates/occam2go/parser/parser_test.go
	if c.Variable != "any" {
		t.Errorf("expected variable 'any', got %q", c.Variable)
	}
}
+
	if c.Variable != "any" {
		t.Errorf("expected variable 'any', got %q", c.Variable)
	}
}

func TestInt16Int32Int64VarDecl(t *testing.T) {
	types := []struct {
		input    string
		expected string
	}{
		{"INT16 x:\n", "INT16"},
		{"INT32 x:\n", "INT32"},
		{"INT64 x:\n", "INT64"},
	}
	for _, tt := range types {
		l := lexer.New(tt.input)
		p := New(l)
		program := p.ParseProgram()
		checkParserErrors(t, p)

		if len(program.Statements) != 1 {
			t.Fatalf("for %q: expected 1 statement, got %d", tt.input, len(program.Statements))
		}
		decl, ok := program.Statements[0].(*ast.VarDecl)
		if !ok {
			t.Fatalf("for %q: expected VarDecl, got %T", tt.input, program.Statements[0])
		}
		if decl.Type != tt.expected {
			t.Errorf("for %q: expected type %s, got %s", tt.input, tt.expected, decl.Type)
		}
	}
}

func TestInt16Int32Int64TypeConversion(t *testing.T) {
	types := []struct {
		input    string
		convType string
	}{
		{"x := INT16 y\n", "INT16"},
		{"x := INT32 y\n", "INT32"},
		{"x := INT64 y\n", "INT64"},
	}
	for _, tt := range types {
		l := lexer.New(tt.input)
		p := New(l)
		program := p.ParseProgram()
		checkParserErrors(t, p)

		if len(program.Statements) != 1 {
			t.Fatalf("for %q: expected 1 statement, got %d", tt.input, len(program.Statements))
		}
		assign, ok := program.Statements[0].(*ast.Assignment)
		if !ok {
			t.Fatalf("for %q: expected Assignment, got %T", tt.input, program.Statements[0])
		}
		conv, ok := assign.Value.(*ast.TypeConversion)
		if !ok {
			t.Fatalf("for %q: expected TypeConversion, got %T", tt.input, assign.Value)
		}
		if conv.TargetType != tt.convType {
			t.Errorf("for %q: expected target type %s, got %s", tt.input, tt.convType, conv.TargetType)
		}
	}
}

func TestMostNegMostPosInt16Int32Int64(t *testing.T) {
	types := []string{"INT16", "INT32", "INT64"}
	for _, typ := range types {
		for _, kw := range []string{"MOSTNEG", "MOSTPOS"} {
			input := "x := " + kw + " " + typ + "\n"
			l := lexer.New(input)
			p := New(l)
			program := p.ParseProgram()
			checkParserErrors(t, p)

			if len(program.Statements) != 1 {
				t.Fatalf("for %q: expected 1 statement, got %d", input, len(program.Statements))
			}
			assign, ok := program.Statements[0].(*ast.Assignment)
			if !ok {
				t.Fatalf("for %q: expected Assignment, got %T", input, program.Statements[0])
			}
			most, ok := assign.Value.(*ast.MostExpr)
			if !ok {
				t.Fatalf("for %q: expected MostExpr, got %T", input, assign.Value)
			}
			if most.ExprType != typ {
				t.Errorf("for %q: expected type %s, got %s", input, typ, most.ExprType)
			}
			expectedNeg := kw == "MOSTNEG"
			if most.IsNeg != expectedNeg {
				t.Errorf("for %q: expected IsNeg=%v, got %v", input, expectedNeg, most.IsNeg)
			}
		}
	}
}
The file /home/david/projects/code-associates/occam2go/parser/parser_test.go has been updated successfully.

Now add codegen unit tests (type conversion and MOSTNEG/MOSTPOS).

✏️ Edit codegen_test.go
/home/david/projects/code-associates/occam2go/codegen/codegen_test.go
		{"x := REAL32 y\n", "x = float32(y)"},
		{"x := REAL64 y\n", "x = float64(y)"},
	}
+
		{"x := REAL32 y\n", "x = float32(y)"},
		{"x := REAL64 y\n", "x = float64(y)"},
		{"x := INT16 y\n", "x = int16(y)"},
		{"x := INT32 y\n", "x = int32(y)"},
		{"x := INT64 y\n", "x = int64(y)"},
	}
The file /home/david/projects/code-associates/occam2go/codegen/codegen_test.go has been updated successfully.
✏️ Edit codegen_test.go
/home/david/projects/code-associates/occam2go/codegen/codegen_test.go
		{"x := MOSTNEG REAL64\n", "x = -math.MaxFloat64"},
		{"x := MOSTPOS REAL64\n", "x = math.MaxFloat64"},
	}
+
		{"x := MOSTNEG REAL64\n", "x = -math.MaxFloat64"},
		{"x := MOSTPOS REAL64\n", "x = math.MaxFloat64"},
		{"x := MOSTNEG INT16\n", "x = math.MinInt16"},
		{"x := MOSTPOS INT16\n", "x = math.MaxInt16"},
		{"x := MOSTNEG INT32\n", "x = math.MinInt32"},
		{"x := MOSTPOS INT32\n", "x = math.MaxInt32"},
		{"x := MOSTNEG INT64\n", "x = math.MinInt64"},
		{"x := MOSTPOS INT64\n", "x = math.MaxInt64"},
	}
The file /home/david/projects/code-associates/occam2go/codegen/codegen_test.go has been updated successfully.

Now add the e2e test.

✏️ Edit e2e_types_test.go
/home/david/projects/code-associates/occam2go/codegen/e2e_types_test.go
    print.bool(neg)
`
	output := transpileCompileRun(t, occam)
	expected := "true\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}
+
    print.bool(neg)
`
	output := transpileCompileRun(t, occam)
	expected := "true\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_Int16VarDeclAndConversion(t *testing.T) {
	occam := `SEQ
  INT16 x:
  x := 1000
  INT y:
  y := INT x
  print.int(y)
`
	output := transpileCompileRun(t, occam)
	expected := "1000\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_Int32VarDeclAndConversion(t *testing.T) {
	occam := `SEQ
  INT32 x:
  x := 100000
  INT y:
  y := INT x
  print.int(y)
`
	output := transpileCompileRun(t, occam)
	expected := "100000\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_Int64VarDeclAndConversion(t *testing.T) {
	occam := `SEQ
  INT64 x:
  x := 123456789
  INT y:
  y := INT x
  print.int(y)
`
	output := transpileCompileRun(t, occam)
	expected := "123456789\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_MostNegMostPosInt16(t *testing.T) {
	occam := `SEQ
  INT16 x:
  x := MOSTNEG INT16
  print.int(INT x)
  x := MOSTPOS INT16
  print.int(INT x)
`
	output := transpileCompileRun(t, occam)
	expected := "-32768\n32767\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_MostNegMostPosInt32(t *testing.T) {
	occam := `SEQ
  INT32 x:
  x := MOSTNEG INT32
  print.int(INT x)
  x := MOSTPOS INT32
  print.int(INT x)
`
	output := transpileCompileRun(t, occam)
	expected := "-2147483648\n2147483647\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}

func TestE2E_Int16TypeConversionFromInt(t *testing.T) {
	occam := `SEQ
  INT n:
  n := 42
  INT16 x:
  x := INT16 n
  print.int(INT x)
`
	output := transpileCompileRun(t, occam)
	expected := "42\n"
	if output != expected {
		t.Errorf("expected %q, got %q", expected, output)
	}
}
The file /home/david/projects/code-associates/occam2go/codegen/e2e_types_test.go has been updated successfully.

5. Update TODO.md

✏️ Edit TODO.md
/home/david/projects/code-associates/occam2go/TODO.md
- **INT, BYTE, BOOL, REAL, REAL32, REAL64** — Scalar types (REAL/REAL64 map to float64, REAL32 maps to float32)
+
- **INT, INT16, INT32, INT64, BYTE, BOOL, REAL, REAL32, REAL64** — Scalar types (INT16/32/64 map to int16/32/64, REAL/REAL64 map to float64, REAL32 maps to float32)
The file /home/david/projects/code-associates/occam2go/TODO.md has been updated successfully.
✏️ Edit TODO.md
/home/david/projects/code-associates/occam2go/TODO.md
- **Type conversions** — `INT expr`, `BYTE expr`, `REAL32 expr`, `REAL64 expr`
+
- **Type conversions** — `INT expr`, `INT16 expr`, `INT32 expr`, `INT64 expr`, `BYTE expr`, `REAL32 expr`, `REAL64 expr`
The file /home/david/projects/code-associates/occam2go/TODO.md has been updated successfully.
✏️ Edit TODO.md
/home/david/projects/code-associates/occam2go/TODO.md
- **MOSTNEG/MOSTPOS** — Type min/max constants for INT, BYTE, REAL32, REAL64
+
- **MOSTNEG/MOSTPOS** — Type min/max constants for INT, INT16, INT32, INT64, BYTE, REAL32, REAL64
The file /home/david/projects/code-associates/occam2go/TODO.md has been updated successfully.

6. Run tests

$ Bash
Run all tests
go test ./... 2>&1
?   	github.com/codeassociates/occam2go	[no test files]
?   	github.com/codeassociates/occam2go/ast	[no test files]
ok  	github.com/codeassociates/occam2go/codegen	17.272s
ok  	github.com/codeassociates/occam2go/lexer	0.001s
ok  	github.com/codeassociates/occam2go/modgen	(cached)
ok  	github.com/codeassociates/occam2go/parser	0.002s
ok  	github.com/codeassociates/occam2go/preproc	(cached)

All tests pass. Now verify the course module still works.

$ Bash
Build and verify course module still compiles
go build -o occam2go . && ./occam2go -I kroc/modules/course/libsrc -D TARGET.BITS.PER.WORD=32 -o /tmp/course_out.go kroc/modules/course/libsrc/course.module && go vet /tmp/course_out.go 2>&1

All clean. Let me also update CLAUDE.md to reflect the new types.

Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/CLAUDE.md",
  "offset": 37,
  "limit": 20
}
    37→   - `preproc.go` — Preprocessor with condition stack and expression evaluator
    38→
    39→2. **`lexer/`** — Tokenizer with indentation tracking. Produces `INDENT`/`DEDENT` tokens from whitespace changes (2-space indent = 1 level). Suppresses INDENT/DEDENT/NEWLINE inside parentheses (`parenDepth` tracking, like Python). Key files:
    40→   - `token.go` — Token types and keyword lookup
    41→   - `lexer.go` — Lexer with `indentStack`, `pendingTokens` queue, and `parenDepth` counter
    42→
    43→3. **`parser/`** — Recursive descent parser with Pratt expression parsing. Produces AST.
    44→   - `parser.go` — All parsing logic in one file
    45→
    46→4. **`ast/`** — AST node definitions. Every construct has a struct.
    47→   - `ast.go` — All node types: `Program`, `SeqBlock`, `ParBlock`, `VarDecl`, `Assignment`, `ProcDecl`, `FuncDecl`, etc.
    48→
    49→5. **`codegen/`** — AST → Go source code. Two-pass: first collects metadata (imports, proc signatures), then generates.
    50→   - `codegen.go` — Generator with `strings.Builder` output
    51→   - `codegen_test.go` — Unit tests (transpile, check output strings)
    52→   - `e2e_test.go` — End-to-end tests (transpile → `go build` → execute → check stdout)
    53→
    54→6. **`modgen/`** — Generates `.module` files from KRoC SConscript build files. Uses regex-based pattern matching (not Python execution) to extract `Split('''...''')` source lists and `OccamLibrary` calls. Only works with simple, declarative SConscript files; files using Python control flow (loops, conditionals) are not supported.
    55→   - `modgen.go` — SConscript parser and module file generator
    56→
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/CLAUDE.md",
  "offset": 60,
  "limit": 30
}
    60→
    61→| Occam | Go |
    62→|---|---|
    63→| `SEQ` | Sequential statements (Go default) |
    64→| `SEQ i = 0 FOR n` | `for i := 0; i < n; i++` |
    65→| `SEQ i = 0 FOR n STEP s` | Counter-based `for` with `i := start + counter * s` |
    66→| `PAR` | goroutines + `sync.WaitGroup` |
    67→| `PAR i = 0 FOR n` | Loop spawning goroutines + WaitGroup |
    68→| `IF` (multi-branch) | `if / else if` chain |
    69→| `WHILE cond` | `for cond` |
    70→| `CASE x` | `switch x` |
    71→| `STOP` | `fmt.Fprintln(os.Stderr, ...)` + `select {}` |
    72→| `ALT` | `select` |
    73→| `ALT i = 0 FOR n` | `reflect.Select` with runtime case slice |
    74→| `CHAN OF INT c:` | `c := make(chan int)` |
    75→| `c ! expr` | `c <- expr` |
    76→| `c ? x` | `x = <-c` |
    77→| `PROC name(...)` | `func name(...)` |
    78→| `INT FUNCTION name(...) IS expr` | `func name(...) int { return expr }` |
    79→| `INT INLINE FUNCTION name(...)` | `func name(...) int { ... }` (INLINE ignored) |
    80→| `INT, INT FUNCTION name(...)` | `func name(...) (int, int) { ... }` |
    81→| `RESULT expr1, expr2` | `return expr1, expr2` |
    82→| `a, b := func(...)` | `a, b = func(...)` (multi-assignment) |
    83→| `x[0], x[1] := x[1], x[0]` | `x[0], x[1] = x[1], x[0]` (indexed multi-assignment) |
    84→| `TIMER` / `tim ? t` | `time.Now().UnixMicro()` |
    85→| `=` / `<>` | `==` / `!=` |
    86→| `AND` / `OR` / `NOT` | `&&` / `||` / `!` |
    87→| `REAL32 x:` / `REAL64 x:` | `var x float32` / `var x float64` |
    88→| `INT expr`, `BYTE expr`, etc. | `int(expr)`, `byte(expr)`, etc. (type conversions) |
    89→| `REAL32 expr` / `REAL64 expr` | `float32(expr)` / `float64(expr)` (type conversions) |
✏️ Edit CLAUDE.md
/home/david/projects/code-associates/occam2go/CLAUDE.md
| `REAL32 x:` / `REAL64 x:` | `var x float32` / `var x float64` |
| `INT expr`, `BYTE expr`, etc. | `int(expr)`, `byte(expr)`, etc. (type conversions) |
| `REAL32 expr` / `REAL64 expr` | `float32(expr)` / `float64(expr)` (type conversions) |
+
| `REAL32 x:` / `REAL64 x:` | `var x float32` / `var x float64` |
| `INT16 x:` / `INT32 x:` / `INT64 x:` | `var x int16` / `var x int32` / `var x int64` |
| `INT expr`, `BYTE expr`, etc. | `int(expr)`, `byte(expr)`, etc. (type conversions) |
| `INT16 expr` / `INT32 expr` / `INT64 expr` | `int16(expr)` / `int32(expr)` / `int64(expr)` (type conversions) |
| `REAL32 expr` / `REAL64 expr` | `float32(expr)` / `float64(expr)` (type conversions) |
The file /home/david/projects/code-associates/occam2go/CLAUDE.md has been updated successfully.

Now update the MOSTNEG/MOSTPOS row and the "What's Implemented" section in CLAUDE.md.

Grep
{
  "pattern": "MOSTNEG INT.*MOSTPOS INT",
  "path": "/home/david/projects/code-associates/occam2go/CLAUDE.md",
  "output_mode": "content"
}
131:| `MOSTNEG INT` / `MOSTPOS INT` | `math.MinInt` / `math.MaxInt` |
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/CLAUDE.md",
  "offset": 131,
  "limit": 8
}
   131→| `MOSTNEG INT` / `MOSTPOS INT` | `math.MinInt` / `math.MaxInt` |
   132→| `MOSTNEG BYTE` / `MOSTPOS BYTE` | `0` / `255` |
   133→| `MOSTNEG REAL32` / `MOSTPOS REAL32` | `-math.MaxFloat32` / `math.MaxFloat32` |
   134→| `MOSTNEG REAL64` / `MOSTPOS REAL64` | `-math.MaxFloat64` / `math.MaxFloat64` |
   135→| `[arr FROM n FOR m]` | `arr[n : n+m]` (array slice) |
   136→| `[arr FOR m]` | `arr[0 : m]` (shorthand slice, FROM 0 implied) |
   137→| `[arr FROM n FOR m] := src` | `copy(arr[n:n+m], src)` (slice assignment) |
   138→| Nested `PROC`/`FUNCTION` | `name := func(...) { ... }` (Go closure) |
✏️ Edit CLAUDE.md
/home/david/projects/code-associates/occam2go/CLAUDE.md
| `MOSTNEG INT` / `MOSTPOS INT` | `math.MinInt` / `math.MaxInt` |
| `MOSTNEG BYTE` / `MOSTPOS BYTE` | `0` / `255` |
| `MOSTNEG REAL32` / `MOSTPOS REAL32` | `-math.MaxFloat32` / `math.MaxFloat32` |
| `MOSTNEG REAL64` / `MOSTPOS REAL64` | `-math.MaxFloat64` / `math.MaxFloat64` |
+
| `MOSTNEG INT` / `MOSTPOS INT` | `math.MinInt` / `math.MaxInt` |
| `MOSTNEG INT16` / `MOSTPOS INT16` | `math.MinInt16` / `math.MaxInt16` |
| `MOSTNEG INT32` / `MOSTPOS INT32` | `math.MinInt32` / `math.MaxInt32` |
| `MOSTNEG INT64` / `MOSTPOS INT64` | `math.MinInt64` / `math.MaxInt64` |
| `MOSTNEG BYTE` / `MOSTPOS BYTE` | `0` / `255` |
| `MOSTNEG REAL32` / `MOSTPOS REAL32` | `-math.MaxFloat32` / `math.MaxFloat32` |
| `MOSTNEG REAL64` / `MOSTPOS REAL64` | `-math.MaxFloat64` / `math.MaxFloat64` |
The file /home/david/projects/code-associates/occam2go/CLAUDE.md has been updated successfully.

Now update the "What's Implemented" section in CLAUDE.md.

Grep
{
  "pattern": "Preprocessor.*INCLUDE.*module file|INT.*BYTE.*BOOL.*REAL",
  "path": "/home/david/projects/code-associates/occam2go/CLAUDE.md",
  "output_mode": "content"
}
164:- `parseExpression()` handles prefix (IDENT, INT, STRING, TRUE/FALSE, LPAREN, MINUS, NOT, BITNOT, INT_TYPE/BYTE_TYPE/BOOL_TYPE/REAL_TYPE for type conversions) then infix loop
180:[Omitted long matching line]
Read
{
  "file_path": "/home/david/projects/code-associates/occam2go/CLAUDE.md",
  "offset": 180,
  "limit": 3
}
   180→Preprocessor (`#IF`/`#ELSE`/`#ENDIF`/`#DEFINE`/`#INCLUDE` with search paths, include guards, include-once deduplication, `#COMMENT`/`#PRAGMA`/`#USE` ignored), module file generation from SConscript (`gen-module` subcommand), SEQ, PAR, IF, WHILE, CASE, ALT (with guards, timer timeouts, multi-statement bodies with scoped declarations, and replicators using `reflect.Select`), SKIP, STOP, variable/array/channel/timer declarations, abbreviations (`VAL INT x IS 42:`, `INT y IS z:`, `VAL []BYTE s IS "hi":`, untyped `VAL x IS expr:`), assignments (simple and indexed), channel send/receive, channel arrays (`[n]CHAN OF TYPE` with indexed send/receive and `[]CHAN OF TYPE` proc params), PROC (with VAL, RESULT, reference, CHAN, []CHAN, open array `[]TYPE`, fixed-size array `[n]TYPE`, and shared-type params), channel direction restrictions (`CHAN OF INT c?` → `<-chan int`, `CHAN OF INT c!` → `chan<- int`, call-site annotations `out!`/`in?` accepted), multi-line parameter lists and expressions (lexer suppresses INDENT/DEDENT/NEWLINE inside parens/brackets and after continuation operators), FUNCTION (IS and VALOF forms with multi-statement bodies, including multi-result `INT, INT FUNCTION` with `RESULT a, b`), multi-assignment (`a, b := func(...)` including indexed targets like `x[0], x[1] := x[1], x[0]`), KRoC-style colon terminators on PROC/FUNCTION (optional), INLINE function modifier (accepted and ignored), replicators on SEQ/PAR/IF/ALT (with optional STEP), arithmetic/comparison/logical/AFTER/bitwise operators, type conversions (`INT expr`, `BYTE expr`, `REAL32 expr`, `REAL64 expr`, etc.), REAL32/REAL64 types, hex integer literals (`#FF`, `#80000000`), string literals, byte literals (`'A'`, `'*n'` with occam escape sequences), built-in print procedures, protocols (simple, sequential, and variant), record types (with field access via bracket syntax), SIZE operator, array slices (`[arr FROM n FOR m]` and shorthand `[arr FOR m]` with slice assignment), array literals (`[1, 2, 3]`), nested PROCs/FUNCTIONs (local definitions as Go closures), MOSTNEG/MOSTPOS (type min/max constants for INT, BYTE, REAL32, REAL64), INITIAL declarations (`INITIAL INT x IS 42:` — mutable variable with initial value), checked (modular) arithmetic (`PLUS`, `MINUS`, `TIMES` — wrapping operators), RETYPES (bit-level type reinterpretation: `VAL INT X RETYPES X :` for float32→int, `VAL [2]INT X RETYPES X :` for float64→int pair), transputer intrinsics (LONGPROD, LONGDIV, LONGSUM, LONGDIFF, NORMALISE, SHIFTRIGHT, SHIFTLEFT — implemented as Go helper functions), CAUSEERROR (maps to `panic("CAUSEERROR")`).
   181→
   182→## Course Module Testing
✏️ Edit CLAUDE.md
/home/david/projects/code-associates/occam2go/CLAUDE.md
type conversions (`INT expr`, `BYTE expr`, `REAL32 expr`, `REAL64 expr`, etc.), REAL32/REAL64 types,
+
type conversions (`INT expr`, `INT16 expr`, `INT32 expr`, `INT64 expr`, `BYTE expr`, `REAL32 expr`, `REAL64 expr`, etc.), INT16/INT32/INT64 types, REAL32/REAL64 types,
The file /home/david/projects/code-associates/occam2go/CLAUDE.md has been updated successfully.
✏️ Edit CLAUDE.md
/home/david/projects/code-associates/occam2go/CLAUDE.md
MOSTNEG/MOSTPOS (type min/max constants for INT, BYTE, REAL32, REAL64)
+
MOSTNEG/MOSTPOS (type min/max constants for INT, INT16, INT32, INT64, BYTE, REAL32, REAL64)
The file /home/david/projects/code-associates/occam2go/CLAUDE.md has been updated successfully.
TaskUpdate
{
  "taskId": "1",
  "status": "completed"
}
Updated task #1 status

All done. Here's a summary of the changes:

Files modified

  1. lexer/token.go — Added INT16_TYPE, INT32_TYPE, INT64_TYPE token types, names, and keyword entries
  2. parser/parser.go — Updated 4 locations:
  3. isTypeToken() — added INT16/32/64 (propagates to ~17 call sites)
  4. parseStatement() switch case — added INT16/32/64
  5. isAltDeclStart() — added INT16/32/64
  6. MOSTNEG/MOSTPOS guard — added INT16/32/64 to peek check
  7. Type conversion prefix case — added INT16/32/64
  8. codegen/codegen.go — Updated 4 functions:
  9. occamTypeToGoBase() — INT16→int16, INT32→int32, INT64→int64
  10. occamTypeToGo() — same mappings
  11. generateMostExpr() — added INT16/32/64 → math.MinInt16/MaxInt16 etc.
  12. parser/parser_test.go — Added 3 tests: var decl, type conversion, MOSTNEG/MOSTPOS
  13. codegen/codegen_test.go — Added INT16/32/64 cases to existing type conversion and MOSTNEG/MOSTPOS test tables
  14. codegen/e2e_types_test.go — Added 6 e2e tests: var decl + conversion for each type, MOSTNEG/MOSTPOS for INT16 and INT32, and type conversion from INT
  15. TODO.md and CLAUDE.md — Updated documentation

All tests pass (go test ./...) and the course module still transpiles and passes go vet.

Thank you. Please create a PR for this feature.

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

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   CLAUDE.md
	modified:   TODO.md
	modified:   codegen/codegen.go
	modified:   codegen/codegen_test.go
	modified:   codegen/e2e_types_test.go
	modified:   lexer/token.go
	modified:   parser/parser.go
	modified:   parser/parser_test.go

no changes added to commit (use "git add" and/or "git commit -a")
$ Bash
Show all unstaged changes
git diff
diff --git a/CLAUDE.md b/CLAUDE.md
index 4c7b3ac..d538033 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -85,7 +85,9 @@ Six packages, one pipeline:
 | `=` / `<>` | `==` / `!=` |
 | `AND` / `OR` / `NOT` | `&&` / `||` / `!` |
 | `REAL32 x:` / `REAL64 x:` | `var x float32` / `var x float64` |
+| `INT16 x:` / `INT32 x:` / `INT64 x:` | `var x int16` / `var x int32` / `var x int64` |
 | `INT expr`, `BYTE expr`, etc. | `int(expr)`, `byte(expr)`, etc. (type conversions) |
+| `INT16 expr` / `INT32 expr` / `INT64 expr` | `int16(expr)` / `int32(expr)` / `int64(expr)` (type conversions) |
 | `REAL32 expr` / `REAL64 expr` | `float32(expr)` / `float64(expr)` (type conversions) |
 | `PROTOCOL X IS INT` | `type _proto_X = int` (simple protocol) |
 | `PROTOCOL X IS INT ; BYTE` | `type _proto_X struct { _0 int; _1 byte }` (sequential) |
@@ -127,6 +129,9 @@ Six packages, one pipeline:
 | `#FF`, `#80000000` | `0xFF`, `0x80000000` (hex integer literals) |
 | `SIZE arr` / `SIZE "str"` | `len(arr)` / `len("str")` |
 | `MOSTNEG INT` / `MOSTPOS INT` | `math.MinInt` / `math.MaxInt` |
+| `MOSTNEG INT16` / `MOSTPOS INT16` | `math.MinInt16` / `math.MaxInt16` |
+| `MOSTNEG INT32` / `MOSTPOS INT32` | `math.MinInt32` / `math.MaxInt32` |
+| `MOSTNEG INT64` / `MOSTPOS INT64` | `math.MinInt64` / `math.MaxInt64` |
 | `MOSTNEG BYTE` / `MOSTPOS BYTE` | `0` / `255` |
 | `MOSTNEG REAL32` / `MOSTPOS REAL32` | `-math.MaxFloat32` / `math.MaxFloat32` |
 | `MOSTNEG REAL64` / `MOSTPOS REAL64` | `-math.MaxFloat64` / `math.MaxFloat64` |
@@ -172,7 +177,7 @@ Typical workflow for a new language construct:
 
 ## What's Implemented
 
-Preprocessor (`#IF`/`#ELSE`/`#ENDIF`/`#DEFINE`/`#INCLUDE` with search paths, include guards, include-once deduplication, `#COMMENT`/`#PRAGMA`/`#USE` ignored), module file generation from SConscript (`gen-module` subcommand), SEQ, PAR, IF, WHILE, CASE, ALT (with guards, timer timeouts, multi-statement bodies with scoped declarations, and replicators using `reflect.Select`), SKIP, STOP, variable/array/channel/timer declarations, abbreviations (`VAL INT x IS 42:`, `INT y IS z:`, `VAL []BYTE s IS "hi":`, untyped `VAL x IS expr:`), assignments (simple and indexed), channel send/receive, channel arrays (`[n]CHAN OF TYPE` with indexed send/receive and `[]CHAN OF TYPE` proc params), PROC (with VAL, RESULT, reference, CHAN, []CHAN, open array `[]TYPE`, fixed-size array `[n]TYPE`, and shared-type params), channel direction restrictions (`CHAN OF INT c?` → `<-chan int`, `CHAN OF INT c!` → `chan<- int`, call-site annotations `out!`/`in?` accepted), multi-line parameter lists and expressions (lexer suppresses INDENT/DEDENT/NEWLINE inside parens/brackets and after continuation operators), FUNCTION (IS and VALOF forms with multi-statement bodies, including multi-result `INT, INT FUNCTION` with `RESULT a, b`), multi-assignment (`a, b := func(...)` including indexed targets like `x[0], x[1] := x[1], x[0]`), KRoC-style colon terminators on PROC/FUNCTION (optional), INLINE function modifier (accepted and ignored), replicators on SEQ/PAR/IF/ALT (with optional STEP), arithmetic/comparison/logical/AFTER/bitwise operators, type conversions (`INT expr`, `BYTE expr`, `REAL32 expr`, `REAL64 expr`, etc.), REAL32/REAL64 types, hex integer literals (`#FF`, `#80000000`), string literals, byte literals (`'A'`, `'*n'` with occam escape sequences), built-in print procedures, protocols (simple, sequential, and variant), record types (with field access via bracket syntax), SIZE operator, array slices (`[arr FROM n FOR m]` and shorthand `[arr FOR m]` with slice assignment), array literals (`[1, 2, 3]`), nested PROCs/FUNCTIONs (local definitions as Go closures), MOSTNEG/MOSTPOS (type min/max constants for INT, BYTE, REAL32, REAL64), INITIAL declarations (`INITIAL INT x IS 42:` — mutable variable with initial value), checked (modular) arithmetic (`PLUS`, `MINUS`, `TIMES` — wrapping operators), RETYPES (bit-level type reinterpretation: `VAL INT X RETYPES X :` for float32→int, `VAL [2]INT X RETYPES X :` for float64→int pair), transputer intrinsics (LONGPROD, LONGDIV, LONGSUM, LONGDIFF, NORMALISE, SHIFTRIGHT, SHIFTLEFT — implemented as Go helper functions), CAUSEERROR (maps to `panic("CAUSEERROR")`).
+Preprocessor (`#IF`/`#ELSE`/`#ENDIF`/`#DEFINE`/`#INCLUDE` with search paths, include guards, include-once deduplication, `#COMMENT`/`#PRAGMA`/`#USE` ignored), module file generation from SConscript (`gen-module` subcommand), SEQ, PAR, IF, WHILE, CASE, ALT (with guards, timer timeouts, multi-statement bodies with scoped declarations, and replicators using `reflect.Select`), SKIP, STOP, variable/array/channel/timer declarations, abbreviations (`VAL INT x IS 42:`, `INT y IS z:`, `VAL []BYTE s IS "hi":`, untyped `VAL x IS expr:`), assignments (simple and indexed), channel send/receive, channel arrays (`[n]CHAN OF TYPE` with indexed send/receive and `[]CHAN OF TYPE` proc params), PROC (with VAL, RESULT, reference, CHAN, []CHAN, open array `[]TYPE`, fixed-size array `[n]TYPE`, and shared-type params), channel direction restrictions (`CHAN OF INT c?` → `<-chan int`, `CHAN OF INT c!` → `chan<- int`, call-site annotations `out!`/`in?` accepted), multi-line parameter lists and expressions (lexer suppresses INDENT/DEDENT/NEWLINE inside parens/brackets and after continuation operators), FUNCTION (IS and VALOF forms with multi-statement bodies, including multi-result `INT, INT FUNCTION` with `RESULT a, b`), multi-assignment (`a, b := func(...)` including indexed targets like `x[0], x[1] := x[1], x[0]`), KRoC-style colon terminators on PROC/FUNCTION (optional), INLINE function modifier (accepted and ignored), replicators on SEQ/PAR/IF/ALT (with optional STEP), arithmetic/comparison/logical/AFTER/bitwise operators, type conversions (`INT expr`, `INT16 expr`, `INT32 expr`, `INT64 expr`, `BYTE expr`, `REAL32 expr`, `REAL64 expr`, etc.), INT16/INT32/INT64 types, REAL32/REAL64 types, hex integer literals (`#FF`, `#80000000`), string literals, byte literals (`'A'`, `'*n'` with occam escape sequences), built-in print procedures, protocols (simple, sequential, and variant), record types (with field access via bracket syntax), SIZE operator, array slices (`[arr FROM n FOR m]` and shorthand `[arr FOR m]` with slice assignment), array literals (`[1, 2, 3]`), nested PROCs/FUNCTIONs (local definitions as Go closures), MOSTNEG/MOSTPOS (type min/max constants for INT, INT16, INT32, INT64, BYTE, REAL32, REAL64), INITIAL declarations (`INITIAL INT x IS 42:` — mutable variable with initial value), checked (modular) arithmetic (`PLUS`, `MINUS`, `TIMES` — wrapping operators), RETYPES (bit-level type reinterpretation: `VAL INT X RETYPES X :` for float32→int, `VAL [2]INT X RETYPES X :` for float64→int pair), transputer intrinsics (LONGPROD, LONGDIV, LONGSUM, LONGDIFF, NORMALISE, SHIFTRIGHT, SHIFTLEFT — implemented as Go helper functions), CAUSEERROR (maps to `panic("CAUSEERROR")`).
 
 ## Course Module Testing
 
diff --git a/TODO.md b/TODO.md
index fb1344f..10bd69d 100644
--- a/TODO.md
+++ b/TODO.md
@@ -13,7 +13,7 @@
 - **STOP** — Error + deadlock
 
 ### Data Types & Declarations
-- **INT, BYTE, BOOL, REAL, REAL32, REAL64** — Scalar types (REAL/REAL64 map to float64, REAL32 maps to float32)
+- **INT, INT16, INT32, INT64, BYTE, BOOL, REAL, REAL32, REAL64** — Scalar types (INT16/32/64 map to int16/32/64, REAL/REAL64 map to float64, REAL32 maps to float32)
 - **Variable declarations** — `INT x, y, z:`
 - **Arrays** — `[n]TYPE arr:` with index expressions
 - **Channels** — `CHAN OF TYPE c:` with send (`!`) and receive (`?`); `CHAN BYTE` shorthand (without `OF`)
@@ -45,9 +45,9 @@
 - **Parenthesized expressions**
 - **Array indexing** — `arr[i]`, `arr[expr]`
 - **String literals** — Double-quoted strings
-- **Type conversions** — `INT expr`, `BYTE expr`, `REAL32 expr`, `REAL64 expr`
+- **Type conversions** — `INT expr`, `INT16 expr`, `INT32 expr`, `INT64 expr`, `BYTE expr`, `REAL32 expr`, `REAL64 expr`
 - **Checked arithmetic** — `PLUS`, `MINUS`, `TIMES` — modular (wrapping) operators
-- **MOSTNEG/MOSTPOS** — Type min/max constants for INT, BYTE, REAL32, REAL64
+- **MOSTNEG/MOSTPOS** — Type min/max constants for INT, INT16, INT32, INT64, BYTE, REAL32, REAL64
 - **SIZE operator** — `SIZE arr`, `SIZE "str"` maps to `len()`
 - **Array slices** — `[arr FROM n FOR m]` with slice assignment
 - **Array literals** — `[1, 2, 3]` — inline array/table expressions
diff --git a/codegen/codegen.go b/codegen/codegen.go
index b246df4..258c986 100644
--- a/codegen/codegen.go
+++ b/codegen/codegen.go
@@ -931,6 +931,24 @@ func (g *Generator) generateMostExpr(e *ast.MostExpr) {
 		} else {
 			g.write("math.MaxInt")
 		}
+	case "INT16":
+		if e.IsNeg {
+			g.write("math.MinInt16")
+		} else {
+			g.write("math.MaxInt16")
+		}
+	case "INT32":
+		if e.IsNeg {
+			g.write("math.MinInt32")
+		} else {
+			g.write("math.MaxInt32")
+		}
+	case "INT64":
+		if e.IsNeg {
+			g.write("math.MinInt64")
+		} else {
+			g.write("math.MaxInt64")
+		}
 	case "BYTE":
 		if e.IsNeg {
 			g.write("0")
@@ -1403,6 +1421,12 @@ func (g *Generator) occamTypeToGoBase(occamType string) string {
 	switch occamType {
 	case "INT":
 		return "int"
+	case "INT16":
+		return "int16"
+	case "INT32":
+		return "int32"
+	case "INT64":
+		return "int64"
 	case "BYTE":
 		return "byte"
 	case "BOOL":
@@ -1422,6 +1446,12 @@ func (g *Generator) occamTypeToGo(occamType string) string {
 	switch occamType {
 	case "INT":
 		return "int"
+	case "INT16":
+		return "int16"
+	case "INT32":
+		return "int32"
+	case "INT64":
+		return "int64"
 	case "BYTE":
 		return "byte"
 	case "BOOL":
diff --git a/codegen/codegen_test.go b/codegen/codegen_test.go
index d911685..f7052fe 100644
--- a/codegen/codegen_test.go
+++ b/codegen/codegen_test.go
@@ -305,6 +305,9 @@ func TestTypeConversion(t *testing.T) {
 		{"x := BOOL flag\n", "x = bool(flag)"},
 		{"x := REAL32 y\n", "x = float32(y)"},
 		{"x := REAL64 y\n", "x = float64(y)"},
+		{"x := INT16 y\n", "x = int16(y)"},
+		{"x := INT32 y\n", "x = int32(y)"},
+		{"x := INT64 y\n", "x = int64(y)"},
 	}
 
 	for _, tt := range tests {
@@ -328,6 +331,12 @@ func TestMostNegMostPos(t *testing.T) {
 		{"x := MOSTPOS REAL32\n", "x = math.MaxFloat32"},
 		{"x := MOSTNEG REAL64\n", "x = -math.MaxFloat64"},
 		{"x := MOSTPOS REAL64\n", "x = math.MaxFloat64"},
+		{"x := MOSTNEG INT16\n", "x = math.MinInt16"},
+		{"x := MOSTPOS INT16\n", "x = math.MaxInt16"},
+		{"x := MOSTNEG INT32\n", "x = math.MinInt32"},
+		{"x := MOSTPOS INT32\n", "x = math.MaxInt32"},
+		{"x := MOSTNEG INT64\n", "x = math.MinInt64"},
+		{"x := MOSTPOS INT64\n", "x = math.MaxInt64"},
 	}
 
 	for _, tt := range tests {
diff --git a/codegen/e2e_types_test.go b/codegen/e2e_types_test.go
index 15f5f80..043c55b 100644
--- a/codegen/e2e_types_test.go
+++ b/codegen/e2e_types_test.go
@@ -400,3 +400,93 @@ func TestE2E_CheckedArithmeticWrapping(t *testing.T) {
 		t.Errorf("expected %q, got %q", expected, output)
 	}
 }
+
+func TestE2E_Int16VarDeclAndConversion(t *testing.T) {
+	occam := `SEQ
+  INT16 x:
+  x := 1000
+  INT y:
+  y := INT x
+  print.int(y)
+`
+	output := transpileCompileRun(t, occam)
+	expected := "1000\n"
+	if output != expected {
+		t.Errorf("expected %q, got %q", expected, output)
+	}
+}
+
+func TestE2E_Int32VarDeclAndConversion(t *testing.T) {
+	occam := `SEQ
+  INT32 x:
+  x := 100000
+  INT y:
+  y := INT x
+  print.int(y)
+`
+	output := transpileCompileRun(t, occam)
+	expected := "100000\n"
+	if output != expected {
+		t.Errorf("expected %q, got %q", expected, output)
+	}
+}
+
+func TestE2E_Int64VarDeclAndConversion(t *testing.T) {
+	occam := `SEQ
+  INT64 x:
+  x := 123456789
+  INT y:
+  y := INT x
+  print.int(y)
+`
+	output := transpileCompileRun(t, occam)
+	expected := "123456789\n"
+	if output != expected {
+		t.Errorf("expected %q, got %q", expected, output)
+	}
+}
+
+func TestE2E_MostNegMostPosInt16(t *testing.T) {
+	occam := `SEQ
+  INT16 x:
+  x := MOSTNEG INT16
+  print.int(INT x)
+  x := MOSTPOS INT16
+  print.int(INT x)
+`
+	output := transpileCompileRun(t, occam)
+	expected := "-32768\n32767\n"
+	if output != expected {
+		t.Errorf("expected %q, got %q", expected, output)
+	}
+}
+
+func TestE2E_MostNegMostPosInt32(t *testing.T) {
+	occam := `SEQ
+  INT32 x:
+  x := MOSTNEG INT32
+  print.int(INT x)
+  x := MOSTPOS INT32
+  print.int(INT x)
+`
+	output := transpileCompileRun(t, occam)
+	expected := "-2147483648\n2147483647\n"
+	if output != expected {
+		t.Errorf("expected %q, got %q", expected, output)
+	}
+}
+
+func TestE2E_Int16TypeConversionFromInt(t *testing.T) {
+	occam := `SEQ
+  INT n:
+  n := 42
+  INT16 x:
+  x := INT16 n
+  print.int(INT x)
+`
+	output := transpileCompileRun(t, occam)
+	expected := "42\n"
+	if output != expected {
+		t.Errorf("expected %q, got %q", expected, output)
+	}
+}
diff --git a/lexer/token.go b/lexer/token.go
index 9bfa27a..4d06150 100644
--- a/lexer/token.go
+++ b/lexer/token.go
@@ -80,6 +80,9 @@ const (
 	REAL_TYPE
 	REAL32_TYPE
 	REAL64_TYPE
+	INT16_TYPE
+	INT32_TYPE
+	INT64_TYPE
 	TIMER
 	AFTER
 	VAL
@@ -170,6 +173,9 @@ var tokenNames = map[TokenType]string{
 	REAL_TYPE:   "REAL",
 	REAL32_TYPE: "REAL32",
 	REAL64_TYPE: "REAL64",
+	INT16_TYPE:  "INT16",
+	INT32_TYPE:  "INT32",
+	INT64_TYPE:  "INT64",
 	TIMER:       "TIMER",
 	AFTER:    "AFTER",
 	VAL:       "VAL",
@@ -218,6 +224,9 @@ var keywords = map[string]TokenType{
 	"REAL":   REAL_TYPE,
 	"REAL32": REAL32_TYPE,
 	"REAL64": REAL64_TYPE,
+	"INT16":  INT16_TYPE,
+	"INT32":  INT32_TYPE,
+	"INT64":  INT64_TYPE,
 	"TIMER":  TIMER,
 	"AFTER": AFTER,
 	"VAL":      VAL,
diff --git a/parser/parser.go b/parser/parser.go
index dcc4f2e..4f7b7d0 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -165,7 +165,8 @@ func (p *Parser) parseStatement() ast.Statement {
 		return p.parseAbbreviation()
 	case lexer.INITIAL:
 		return p.parseInitialDecl()
-	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE:
+	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE,
+		lexer.INT16_TYPE, lexer.INT32_TYPE, lexer.INT64_TYPE:
 		if p.peekTokenIs(lexer.FUNCTION) || p.peekTokenIs(lexer.FUNC) || p.peekTokenIs(lexer.COMMA) || p.peekTokenIs(lexer.INLINE) {
 			return p.parseFuncDecl()
 		}
@@ -1650,7 +1651,8 @@ func (p *Parser) parseAltCases() []ast.AltCase {
 
 func (p *Parser) isAltDeclStart() bool {
 	switch p.curToken.Type {
-	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE:
+	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE,
+		lexer.INT16_TYPE, lexer.INT32_TYPE, lexer.INT64_TYPE:
 		return true
 	case lexer.VAL:
 		return true
@@ -1979,7 +1981,8 @@ procBodyDone:
 func isTypeToken(t lexer.TokenType) bool {
 	return t == lexer.INT_TYPE || t == lexer.BYTE_TYPE ||
 		t == lexer.BOOL_TYPE || t == lexer.REAL_TYPE ||
-		t == lexer.REAL32_TYPE || t == lexer.REAL64_TYPE
+		t == lexer.REAL32_TYPE || t == lexer.REAL64_TYPE ||
+		t == lexer.INT16_TYPE || t == lexer.INT32_TYPE || t == lexer.INT64_TYPE
 }
 
 func (p *Parser) parseProcParams() []ast.ProcParam {
@@ -2794,7 +2797,8 @@ func (p *Parser) parseExpression(precedence int) ast.Expression {
 		// Expect a type name next
 		if !p.peekTokenIs(lexer.INT_TYPE) && !p.peekTokenIs(lexer.BYTE_TYPE) &&
 			!p.peekTokenIs(lexer.BOOL_TYPE) && !p.peekTokenIs(lexer.REAL_TYPE) &&
-			!p.peekTokenIs(lexer.REAL32_TYPE) && !p.peekTokenIs(lexer.REAL64_TYPE) {
+			!p.peekTokenIs(lexer.REAL32_TYPE) && !p.peekTokenIs(lexer.REAL64_TYPE) &&
+			!p.peekTokenIs(lexer.INT16_TYPE) && !p.peekTokenIs(lexer.INT32_TYPE) && !p.peekTokenIs(lexer.INT64_TYPE) {
 			p.addError(fmt.Sprintf("expected type after %s, got %s", token.Literal, p.peekToken.Type))
 			return nil
 		}
@@ -2804,7 +2808,8 @@ func (p *Parser) parseExpression(precedence int) ast.Expression {
 			ExprType: p.curToken.Literal,
 			IsNeg:    isNeg,
 		}
-	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE:
+	case lexer.INT_TYPE, lexer.BYTE_TYPE, lexer.BOOL_TYPE, lexer.REAL_TYPE, lexer.REAL32_TYPE, lexer.REAL64_TYPE,
+		lexer.INT16_TYPE, lexer.INT32_TYPE, lexer.INT64_TYPE:
 		token := p.curToken
 		p.nextToken()
 		left = &ast.TypeConversion{
diff --git a/parser/parser_test.go b/parser/parser_test.go
index 8d962b3..e0fcfc5 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -3312,3 +3312,95 @@ func TestAltReplicatorWithAbbreviation(t *testing.T) {
 		t.Errorf("expected variable 'any', got %q", c.Variable)
 	}
 }
+
+func TestInt16Int32Int64VarDecl(t *testing.T) {
+	types := []struct {
+		input    string
+		expected string
+	}{
+		{"INT16 x:\n", "INT16"},
+		{"INT32 x:\n", "INT32"},
+		{"INT64 x:\n", "INT64"},
+	}
+	for _, tt := range types {
+		l := lexer.New(tt.input)
+		p := New(l)
+		program := p.ParseProgram()
+		checkParserErrors(t, p)
+
+		if len(program.Statements) != 1 {
+			t.Fatalf("for %q: expected 1 statement, got %d", tt.input, len(program.Statements))
+		}
+		decl, ok := program.Statements[0].(*ast.VarDecl)
+		if !ok {
+			t.Fatalf("for %q: expected VarDecl, got %T", tt.input, program.Statements[0])
+		}
+		if decl.Type != tt.expected {
+			t.Errorf("for %q: expected type %s, got %s", tt.input, tt.expected, decl.Type)
+		}
+	}
+}
+
+func TestInt16Int32Int64TypeConversion(t *testing.T) {
+	types := []struct {
+		input    string
+		convType string
+	}{
+		{"x := INT16 y\n", "INT16"},
+		{"x := INT32 y\n", "INT32"},
+		{"x := INT64 y\n", "INT64"},
+	}
+	for _, tt := range types {
+		l := lexer.New(tt.input)
+		p := New(l)
+		program := p.ParseProgram()
+		checkParserErrors(t, p)
+
+		if len(program.Statements) != 1 {
+			t.Fatalf("for %q: expected 1 statement, got %d", tt.input, len(program.Statements))
+		}
+		assign, ok := program.Statements[0].(*ast.Assignment)
+		if !ok {
+			t.Fatalf("for %q: expected Assignment, got %T", tt.input, program.Statements[0])
+		}
+		conv, ok := assign.Value.(*ast.TypeConversion)
+		if !ok {
+			t.Fatalf("for %q: expected TypeConversion, got %T", tt.input, assign.Value)
+		}
+		if conv.TargetType != tt.convType {
+			t.Errorf("for %q: expected target type %s, got %s", tt.input, tt.convType, conv.TargetType)
+		}
+	}
+}
+
+func TestMostNegMostPosInt16Int32Int64(t *testing.T) {
+	types := []string{"INT16", "INT32", "INT64"}
+	for _, typ := range types {
+		for _, kw := range []string{"MOSTNEG", "MOSTPOS"} {
+			input := "x := " + kw + " " + typ + "\n"
+			l := lexer.New(input)
+			p := New(l)
+			program := p.ParseProgram()
+			checkParserErrors(t, p)
+
+			if len(program.Statements) != 1 {
+				t.Fatalf("for %q: expected 1 statement, got %d", input, len(program.Statements))
+			}
+			assign, ok := program.Statements[0].(*ast.Assignment)
+			if !ok {
+				t.Fatalf("for %q: expected Assignment, got %T", input, program.Statements[0])
+			}
+			most, ok := assign.Value.(*ast.MostExpr)
+			if !ok {
+				t.Fatalf("for %q: expected MostExpr, got %T", input, assign.Value)
+			}
+			if most.ExprType != typ {
+				t.Errorf("for %q: expected type %s, got %s", input, typ, most.ExprType)
+			}
+			expectedNeg := kw == "MOSTNEG"
+			if most.IsNeg != expectedNeg {
+				t.Errorf("for %q: expected IsNeg=%v, got %v", input, expectedNeg, most.IsNeg)
+			}
+		}
+	}
+}
$ Bash
Show recent commits
git log --oneline -5
5f68a88 Merge pull request #48 from codeassociates/feat/alt-replicator
8f8d542 Support ALT replicators (ALT i = 0 FOR n)
3cbf1cd Merge pull request #47 from codeassociates/feat/inline-function-modifier
14c62e1 Support INLINE function modifier
a6f7350 Merge pull request #42 from codeassociates/feat/source-map-error-messages
$ Bash
Create feature branch
git checkout -b feat/int16-int32-int64-types
Switched to a new branch 'feat/int16-int32-int64-types'
$ Bash
Stage all changed files
git add lexer/token.go parser/parser.go parser/parser_test.go codegen/codegen.go codegen/codegen_test.go codegen/e2e_types_test.go TODO.md CLAUDE.md
$ Bash
Commit all changes
git commit -m "$(cat <<'EOF'
Support INT16, INT32, INT64 integer types

These are standard occam explicit-width integer types needed for
real-world programs (e.g., cast.occ, commstime.occ). They receive the
same treatment as INT, BYTE, REAL32, REAL64: variable declarations,
type conversions, MOSTNEG/MOSTPOS constants, and channel/proc params.

Closes #46

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"
8 files changed, 249 insertions(+), 9 deletions(-)
$ Bash
Push feature branch to remote
git push -u origin feat/int16-int32-int64-types
remote: 
remote: Create a pull request for 'feat/int16-int32-int64-types' on GitHub by visiting:        
remote:      https://github.com/codeassociates/occam2go/pull/new/feat/int16-int32-int64-types        
remote: 
To associate-1.github.com:codeassociates/occam2go.git
 * [new branch]      feat/int16-int32-int64-types -> feat/int16-int32-int64-types
branch 'feat/int16-int32-int64-types' set up to track 'origin/feat/int16-int32-int64-types'.
$ Bash
Create pull request
gh pr create --title "Support INT16, INT32, INT64 integer types" --body "$(cat <<'EOF'
## Summary

- Add `INT16`, `INT32`, `INT64` as recognized type keywords across lexer, parser, and codegen
- Support variable declarations (`INT32 x:`), type conversions (`INT16 expr`), and `MOSTNEG`/`MOSTPOS` constants for all three types
- Maps to Go `int16`, `int32`, `int64` and `math.MinInt16`/`math.MaxInt16`, etc.

Closes #46

## Test plan

- [x] Parser unit tests: var decl, type conversion, MOSTNEG/MOSTPOS parsing for INT16/32/64
- [x] Codegen unit tests: type conversion output and MOSTNEG/MOSTPOS output strings
- [x] E2E tests: declare, assign, convert, and print INT16/32/64 values; verify MOSTNEG/MOSTPOS INT16/INT32
- [x] All existing tests pass (`go test ./...`)
- [x] Course module still transpiles and passes `go vet`

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

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

Thank you. I've approved the PR. Please merge.

$ Bash
Merge PR #49
gh pr merge 49 --merge

PR #49 merged.

Caveat: The messages below were generated by the user while running local commands. DO NOT respond to these messages or otherwise consider them in your response unless the user explicitly asks you to.

/exit exit