Implement the following plan:
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.
lexer/token.goAdd 3 new token types and keyword entries:
INT16_TYPE, INT32_TYPE, INT64_TYPE after REAL64_TYPE"INT16", "INT32", "INT64" entries"INT16": INT16_TYPE, "INT32": INT32_TYPE, "INT64": INT64_TYPEparser/parser.goisTypeToken() (~line 1978): Add || t == lexer.INT16_TYPE || t == lexer.INT32_TYPE || t == lexer.INT64_TYPE — this single change propagates to all ~17 call sites!p.peekTokenIs(lexer.INT16_TYPE) && !p.peekTokenIs(lexer.INT32_TYPE) && !p.peekTokenIs(lexer.INT64_TYPE) to the guard checklexer.INT16_TYPE, lexer.INT32_TYPE, lexer.INT64_TYPE to the case listast/ast.goNo changes needed — types stored as strings.
codegen/codegen.gooccamTypeToGoBase() (~line 1400): Add cases "INT16" → "int16", "INT32" → "int32", "INT64" → "int64"occamTypeToGo() (~line 1421): Add same casesgenerateMostExpr() (~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")parser/parser_test.go): Add test for INT16/INT32/INT64 variable declarations and type conversionscodegen/codegen_test.go): Add test for INT16/INT32/INT64 code generationcodegen/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 runsTODO.md to mark INT16/INT32/INT64 as supportedgo 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
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.
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→{
"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→ }{
"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){
"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-{
"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,{
"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() { 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→{
"subject": "Implement INT16, INT32, INT64 integer types",
"activeForm": "Implementing INT16/INT32/INT64 types"
}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.
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.
"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.
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>
{
"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→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.
{
"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→ 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.
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).
{
"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(){
"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 { 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.
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.
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.
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.
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.
Let me find the test files to see the patterns used.
{
"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:{
"pattern": "func Test.*TypeConv|func Test.*MOST",
"path": "/home/david/projects/code-associates/occam2go/codegen/codegen_test.go",
"output_mode": "content",
"-A": 2
}{
"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{
"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 {{
"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"){
"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]){
"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.
{
"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→{
"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.
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
{
"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→}{
"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{
"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→ 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.
{"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.
{"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.
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.
- **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.
- **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.
- **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.
? 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)
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
{
"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→{
"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) || `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.
{
"pattern": "MOSTNEG INT.*MOSTPOS INT",
"path": "/home/david/projects/code-associates/occam2go/CLAUDE.md",
"output_mode": "content"
}{
"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) || `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.
{
"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]
{
"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 Testingtype 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.
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.
All done. Here's a summary of the changes:
lexer/token.go — Added INT16_TYPE, INT32_TYPE, INT64_TYPE token types, names, and keyword entriesparser/parser.go — Updated 4 locations:isTypeToken() — added INT16/32/64 (propagates to ~17 call sites)parseStatement() switch case — added INT16/32/64isAltDeclStart() — added INT16/32/64codegen/codegen.go — Updated 4 functions:occamTypeToGoBase() — INT16→int16, INT32→int32, INT64→int64occamTypeToGo() — same mappingsgenerateMostExpr() — added INT16/32/64 → math.MinInt16/MaxInt16 etc.parser/parser_test.go — Added 3 tests: var decl, type conversion, MOSTNEG/MOSTPOScodegen/codegen_test.go — Added INT16/32/64 cases to existing type conversion and MOSTNEG/MOSTPOS test tablescodegen/e2e_types_test.go — Added 6 e2e tests: var decl + conversion for each type, MOSTNEG/MOSTPOS for INT16 and INT32, and type conversion from INTTODO.md and CLAUDE.md — Updated documentationAll tests pass (go test ./...) and the course module still transpiles and passes go vet.
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")
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)
+ }
+ }
+ }
+}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
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
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(-)
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'.
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 )"