title: Investigate which language features are blocking mandelbrot example state: OPEN author: dboreham labels: comments: 0 assignees: projects: milestone: number: 70 -- This example program from the kroc project implements the Mandelbrot set computation: https://github.com/concurrency/kroc/blob/master/demos/ttygames/mandelbrot.occ Currently it throws various parse errors when the transpiler processes it. It would be useful to know which unimplemented language features are blocking clean processing of this example.
{
"subagent_type": "Bash",
"prompt": "Download the mandelbrot.occ file from https://raw.githubusercontent.com/concurrency/kroc/master/demos/ttygames/mandelbrot.occ and save it to /tmp/mandelbrot.occ. Then cat the file."
}{
"subagent_type": "Bash",
"prompt": "In /home/david/projects/code-associates/occam2go, first build the transpiler with `go build -o occam2go .`, then try to transpile:\n```\ncurl -sL https://raw.githubusercontent.com/concurrency/kroc/master/demos/ttygames/mandelbrot.occ -o /tmp/mandelbrot.occ\n./occam2go -o /tmp/mandelbrot_out.go /tmp/mandelbrot.occ 2>&1\n```\nShow all the output including any errors."
}[
{
"type": "text",
"text": "The file has been downloaded to `/tmp/mandelbrot.occ` and its contents are displayed above. It is a mandelbrot set renderer written in occam, originally by Jim Moores (1997) with modifications by Fred Barnes. \n\nKey things to note about this file:\n\n- It uses `#INCLUDE \"course.module\"` (the KRoC course module)\n- It defines a `COMPLEX64` record type with `real` and `imag` fields\n- It uses several advanced occam features not yet implemented in occam2go, including:\n - **`DATA TYPE`** (named record types with `DATA TYPE COMPLEX64 RECORD ...`)\n - **`CHAN TYPE`** / **`MOBILE RECORD`** (mobile channel bundles like `CT.WORK.IN`, `CT.WORK.OUT`)\n - **`SHARED`** channel ends\n - **`CLAIM`** for shared channel access\n - **Operator overloading** (e.g., `COMPLEX64 INLINE FUNCTION \"+\"` and `\"**\"`)\n - **`RESCHEDULE()`** intrinsic\n - **`MOBILE`** allocation\n - **Array comprehensions** (`[i = 0 FOR 4 | 0.0]`)\n - **Type decorators** on array literals (`[x,y](COMPLEX64)`)\n - **`cursor.invisible`**, **`cursor.x.y`**, **`erase.screen`**, **`cursor.visible`**, **`flush`** from the course module"
},
{
"type": "text",
"text": "agentId: a4ffa646b4e495bd5 (for resuming to continue this agent's work if needed)\n<usage>total_tokens: 13659\ntool_uses: 2\nduration_ms: 47516</usage>"
}
][
{
"type": "text",
"text": "Here is the full output from the transpilation attempt.\n\n**Build**: Succeeded -- `go build -o occam2go .` completed without errors.\n\n**Transpilation**: Failed with many parse errors. The mandelbrot.occ file uses several occam language features that are not yet implemented in the transpiler. Looking at the error lines, the major missing features include:\n\n1. **DATA TYPE declarations** (line 28, 30) -- occam `DATA TYPE` for defining structured types (different from the existing `RECORD` support).\n\n2. **Array-typed protocol fields** (lines 43, 45, 46) -- e.g., `PROTOCOL` with `[n]INT` array fields. The parser does not handle array types in protocol definitions.\n\n3. **COMPLEX (compound) data type operations** (lines 59, 60, 62, etc.) -- The file defines `COMPLEX` as a `DATA TYPE` with field operations, inline functions that return compound types, and uses bracket-based field access on data types.\n\n4. **INITIAL with array types** (line 73, 153) -- `INITIAL [2]INT x IS [...]:` -- the parser expects a simple type after `INITIAL`, not an array type.\n\n5. **Hexadecimal with `ILLEGAL` tokens** (lines 76, 103, 116-120, 143, 148-149) -- Several hex literals appear to be tokenized as `ILLEGAL`, suggesting the lexer is not handling some hex literal formats correctly.\n\n6. **Protocol channel params with direction** (lines 115, 160) -- Channel parameters typed with a protocol name plus direction markers (e.g., `CHAN OF COMPLEX.JOB job.in?, job.out!`).\n\n7. **Multi-dimensional array access** (line 179) -- `framebuffer[...][...]` nested indexing.\n\n8. **Timer AFTER in ALT guards** (line 187) -- `tim ? AFTER timeout` pattern in ALT cases.\n\n9. **Complex IF guard expressions** (line 218) -- Parenthesized boolean expressions in IF guards with type conversions.\n\n10. **DATA TYPE field access patterns** -- The `DATA TYPE` construct and its associated field access syntax are fundamentally different from `RECORD` and are not supported.\n\nThe mandelbrot demo exercises significantly more of the occam language than what the transpiler currently handles, particularly around `DATA TYPE`, compound/array protocol fields, and multi-dimensional array access."
},
{
"type": "text",
"text": "agentId: ac61656e4b6302a71 (for resuming to continue this agent's work if needed)\n<usage>total_tokens: 15830\ntool_uses: 6\nduration_ms: 64505</usage>"
}
]Let me look at the actual error output more carefully and also read the mandelbrot.occ file to give a precise analysis.
./occam2go -o /tmp/mandelbrot_out.go /tmp/mandelbrot.occ 2>&1
1→--
2→-- mandelbrot.occ -- text example of a mandelbrot set in occam
3→-- Copyright (C) 1997 Jim Moores
4→-- COMPLEX64 code Copyright (C) 1997 D.C. Wood <D.C.Wood@ukc.ac.uk>
5→-- (distributed version, client)
6→-- Modifications (C) 2001, 2007 Fred Barnes
7→--
8→-- This program is free software; you can redistribute it and/or modify
9→-- it under the terms of the GNU General Public License as published by
10→-- the Free Software Foundation; either version 2 of the License, or
11→-- (at your option) any later version.
12→--
13→-- This program is distributed in the hope that it will be useful,
14→-- but WITHOUT ANY WARRANTY; without even the implied warranty of
15→-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16→-- GNU General Public License for more details.
17→--
18→-- You should have received a copy of the GNU General Public License
19→-- along with this program; if not, write to the Free Software
20→-- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21→--
22→
23→
24→#INCLUDE "course.module"
25→
26→
27→DATA TYPE COMPLEX64
28→ RECORD
29→ REAL64 real, imag:
30→:
31→
32→VAL INT IMAGE.WIDTH IS 100:
33→VAL INT IMAGE.HEIGHT IS 50:
34→
35→VAL INT N.WORKERS IS 32:
36→VAL INT N.ITERS IS 850:
37→VAL INT MAX.FRAMES IS 8:
38→
39→VAL INT FRAME.SPEED IS 40000:
40→
41→VAL INT LINES.PER.WORKER IS (IMAGE.HEIGHT / N.WORKERS):
42→
43→VAL [][5]BYTE ansicolours IS ["*#1B[31m", "*#1B[32m", "*#1B[33m", "*#1B[36m", "*#1B[35m", "*#1B[34m"]:
44→
45→PROTOCOL P.JOB.IN IS INT; INT; [4]REAL64:
46→PROTOCOL P.JOB.OUT IS INT; INT; [IMAGE.WIDTH * 2]BYTE:
47→
48→CHAN TYPE CT.WORK.IN
49→ MOBILE RECORD
50→ CHAN INT notify?:
51→ CHAN P.JOB.IN in!:
52→:
53→
54→CHAN TYPE CT.WORK.OUT
55→ MOBILE RECORD
56→ CHAN P.JOB.OUT out?:
57→:
58→
59→REAL64 INLINE FUNCTION COMPLEX64ABSSQ (VAL COMPLEX64 x) IS
60→ (x[real]*x[real]) + (x[imag]*x[imag]):
61→
62→COMPLEX64 INLINE FUNCTION "+" (VAL COMPLEX64 x, y) IS
63→ [x[real] + y[real], x[imag] + y[imag]]:
64→
65→COMPLEX64 INLINE FUNCTION "**" (VAL COMPLEX64 x, y) IS
66→ [(x[real]*y[real]) - (x[imag]*y[imag]),
67→ (x[imag]*y[real]) + (x[real]*y[imag])]:
68→
69→
70→INT FUNCTION calc (VAL COMPLEX64 a)
71→ INT count:
72→ VALOF
73→ INITIAL COMPLEX64 iter IS [0.0, 0.0]:
74→ SEQ
75→ count := 0
76→ WHILE ((COMPLEX64ABSSQ (iter) < 32.0) AND (count < 240))
77→ SEQ
78→ iter := (iter * iter) + a
79→ count := count + 1
80→ RESULT count
81→:
82→
83→PROC worker (SHARED CT.WORK.IN! link.in, SHARED CT.WORK.OUT! link.out)
84→ VAL []BYTE table IS [' ','.',',','*'','~','*"','^',':',';','|','&','[','$','%','@','#']:
85→ BOOL running:
86→ SEQ
87→ running := TRUE
88→ WHILE running
89→ [4]REAL64 job:
90→ [IMAGE.WIDTH * 2]BYTE output:
91→ INT jobid, frame:
92→ SEQ
93→ CLAIM link.in
94→ link.in[in] ? frame; jobid; job
95→
96→ IF
97→ (jobid = (-1)) OR (frame = (-1))
98→ running := FALSE
99→ TRUE
100→ SEQ
101→ VAL REAL64 y IS (job[0] / job[1]) - job[2]:
102→ SEQ i = 0 FOR IMAGE.WIDTH
103→ VAL REAL64 x IS ((REAL64 TRUNC (i - (IMAGE.WIDTH / 2))) / (job[1] * 2.0)) - job[3]:
104→ INT val:
105→ SEQ
106→ val := calc ([x,y](COMPLEX64)) \ 16
107→ output[i*2] := BYTE (val \ 6)
108→ output[(i*2)+1] := table[val]
109→ RESCHEDULE ()
110→
111→ CLAIM link.out
112→ link.out[out] ! frame; jobid; output
113→:
114→
115→PROC farmer (CT.WORK.IN? link.in, CHAN INT tick!, tock?, kill!)
116→ INITIAL REAL64 zoom IS 16.0:
117→ INITIAL REAL64 x.drift IS 0.0:
118→ INITIAL REAL64 y.drift IS 0.0:
119→ VAL REAL64 x.target IS 1.16000014859:
120→ VAL REAL64 y.target IS -0.27140215303:
121→ SEQ
122→ SEQ n = 0 FOR N.ITERS
123→ SEQ
124→ --{{{ generate jobs
125→ SEQ
126→ tick ! n
127→ INT any:
128→ tock ? any
129→
130→ SEQ j = 0 FOR IMAGE.HEIGHT
131→ -- give out jobs
132→ SEQ
133→ [4]REAL64 wpacket:
134→ SEQ
135→ wpacket[0] := REAL64 TRUNC (j - (IMAGE.HEIGHT / 2))
136→ wpacket[1] := zoom
137→ wpacket[2] := y.drift
138→ wpacket[3] := x.drift
139→
140→ link.in[in] ! n; j; wpacket
141→ --}}}
142→
143→ zoom := zoom + (zoom / 32.0)
144→
145→ INITIAL REAL64 diff.x IS (x.target - x.drift):
146→ INITIAL REAL64 diff.y IS (y.target - y.drift):
147→ SEQ
148→ x.drift := x.drift + (diff.x / 16.0)
149→ y.drift := y.drift + (diff.y / 16.0)
150→
151→ --{{{ gracefully shut-down workers
152→ SEQ n = 0 FOR N.WORKERS
153→ INITIAL [4]REAL64 wpacket IS [i = 0 FOR 4 | 0.0]:
154→ link.in[in] ! -1; -1; wpacket
155→ --}}}
156→
157→ kill ! 0
158→:
159→
160→PROC harvester (CT.WORK.OUT? link.out, CHAN INT tick?, tock!, kill?, CHAN BYTE scr!)
161→ [MAX.FRAMES][IMAGE.HEIGHT][IMAGE.WIDTH * 2]BYTE framebuffer:
162→ [MAX.FRAMES]INT fcount:
163→ TIMER tim:
164→ INT t:
165→ VAL INT frame.speed IS FRAME.SPEED:
166→ SEQ
167→ SEQ i = 0 FOR SIZE fcount
168→ fcount[i] := IMAGE.HEIGHT
169→
170→ tim ? t
171→ t := t PLUS frame.speed
172→
173→ INITIAL INT lframe IS 0:
174→ INITIAL INT hframe IS 0:
175→ INITIAL BOOL running IS TRUE:
176→ WHILE running
177→ ALT
178→ INT id, frame:
179→ link.out[out] ? frame; id; framebuffer[frame \ MAX.FRAMES][id]
180→ SEQ
181→ fcount[frame \ MAX.FRAMES] := fcount[frame \ MAX.FRAMES] - 1
182→ IF
183→ fcount[lframe \ MAX.FRAMES] = 0
184→ -- got all results for the lowest frame
185→ INITIAL INT lbuf IS (lframe \ MAX.FRAMES):
186→ SEQ
187→ tim ? AFTER t
188→
189→ fcount[lbuf] := IMAGE.HEIGHT
190→ --{{{ dump framebuffer[lbuf]
191→ cursor.x.y (1, 1, scr!)
192→
193→ INITIAL BYTE cpix IS #FF:
194→ SEQ j = 0 FOR IMAGE.HEIGHT
195→ SEQ
196→ SEQ i = 0 FOR IMAGE.WIDTH
197→ BYTE v:
198→ SEQ
199→ v := framebuffer[lbuf][j][i*2]
200→ IF
201→ cpix <> v
202→ SEQ
203→ cpix := v
204→ out.string (ansicolours[INT cpix], 0, scr!)
205→ TRUE
206→ SKIP
207→ scr ! framebuffer[lbuf][j][(i*2)+1]
208→ scr ! '*n'
209→ -- normalise colour
210→ out.string ("*#1B[0m", 0, scr!)
211→ flush (scr!)
212→ --}}}
213→ lframe := lframe + 1
214→
215→ t := t PLUS frame.speed
216→ TRUE
217→ SKIP
218→ (hframe <= (lframe + 4)) & tick ? hframe
219→ tock ! hframe
220→ INT any:
221→ kill ? any
222→ running := FALSE
223→:
224→
225→
226→PROC mandelbrot (CHAN BYTE kyb?, scr!, err!)
227→ CT.WORK.IN? mwsvr.in:
228→ CT.WORK.OUT? mwsvr.out:
229→ SHARED CT.WORK.IN! mwcli.in:
230→ SHARED CT.WORK.OUT! mwcli.out:
231→ SEQ
232→ mwsvr.in, mwcli.in := MOBILE CT.WORK.IN
233→ mwsvr.out, mwcli.out := MOBILE CT.WORK.OUT
234→
235→ cursor.invisible (scr!)
236→ erase.screen (scr!)
237→
238→ CHAN INT tick, tock, kill:
239→ PAR
240→ farmer (mwsvr.in, tick!, tock?, kill!)
241→ harvester (mwsvr.out, tick?, tock!, kill?, scr!)
242→ PAR i = 0 FOR N.WORKERS
243→ worker (mwcli.in, mwcli.out)
244→
245→ cursor.visible (scr!)
246→:
247→
248→
249→Exit code 1 Preprocessor error: line 24: cannot find included file "course.module" Preprocessor error: line 24: cannot find included file "course.module"
./occam2go -I kroc/modules/course/libsrc -o /tmp/mandelbrot_out.go /tmp/mandelbrot.occ 2>&1
Exit code 1 Parse errors: /tmp/mandelbrot.occ:28: expected IDENT, got NEWLINE /tmp/mandelbrot.occ:30: unexpected token: : /tmp/mandelbrot.occ:43: expected type after VAL, got [ /tmp/mandelbrot.occ:43: unexpected token: INT /tmp/mandelbrot.occ:43: unexpected token: ] /tmp/mandelbrot.occ:45: expected type name in protocol, got [ /tmp/mandelbrot.occ:45: unexpected token: INT /tmp/mandelbrot.occ:45: unexpected token: ] /tmp/mandelbrot.occ:45: expected IDENT, got : /tmp/mandelbrot.occ:45: unexpected token: : /tmp/mandelbrot.occ:46: expected type name in protocol, got [ /tmp/mandelbrot.occ:46: unexpected token: * /tmp/mandelbrot.occ:46: unexpected token: INT /tmp/mandelbrot.occ:46: unexpected token: ] /tmp/mandelbrot.occ:46: expected IDENT, got : /tmp/mandelbrot.occ:46: unexpected token: : /tmp/mandelbrot.occ:48: expected :, got NEWLINE /tmp/mandelbrot.occ:49: expected IDENT, got NEWLINE /tmp/mandelbrot.occ:50: expected :, got ? /tmp/mandelbrot.occ:50: unexpected token: ? /tmp/mandelbrot.occ:50: unexpected token: : /tmp/mandelbrot.occ:51: expected :, got ! /tmp/mandelbrot.occ:51: unexpected token: ! /tmp/mandelbrot.occ:51: unexpected token: : /tmp/mandelbrot.occ:52: unexpected token: : /tmp/mandelbrot.occ:54: expected :, got NEWLINE /tmp/mandelbrot.occ:55: expected IDENT, got NEWLINE /tmp/mandelbrot.occ:56: expected :, got ? /tmp/mandelbrot.occ:56: unexpected token: ? /tmp/mandelbrot.occ:56: unexpected token: : /tmp/mandelbrot.occ:57: unexpected token: : /tmp/mandelbrot.occ:59: expected type in parameter, got IDENT /tmp/mandelbrot.occ:59: expected ), got IDENT /tmp/mandelbrot.occ:59: unexpected token: ) /tmp/mandelbrot.occ:59: unexpected token: IS /tmp/mandelbrot.occ:60: unexpected token: ( /tmp/mandelbrot.occ:60: expected :=, !, or ? after x[...], got * /tmp/mandelbrot.occ:60: unexpected token: * /tmp/mandelbrot.occ:60: expected :=, !, or ? after x[...], got ) /tmp/mandelbrot.occ:60: unexpected token: ) /tmp/mandelbrot.occ:60: unexpected token: + /tmp/mandelbrot.occ:60: unexpected token: ( /tmp/mandelbrot.occ:60: expected :=, !, or ? after x[...], got * /tmp/mandelbrot.occ:60: unexpected token: * /tmp/mandelbrot.occ:60: expected :=, !, or ? after x[...], got ) /tmp/mandelbrot.occ:60: unexpected token: ) /tmp/mandelbrot.occ:60: unexpected token: : /tmp/mandelbrot.occ:62: unexpected token: INLINE /tmp/mandelbrot.occ:62: unexpected token: FUNCTION /tmp/mandelbrot.occ:62: unexpected token: STRING /tmp/mandelbrot.occ:62: unexpected token: ( /tmp/mandelbrot.occ:62: expected type after VAL, got IDENT /tmp/mandelbrot.occ:62: unexpected token in expression: IS /tmp/mandelbrot.occ:63: expected ], got , /tmp/mandelbrot.occ:63: unexpected token: , /tmp/mandelbrot.occ:63: expected :=, !, or ? after x[...], got + /tmp/mandelbrot.occ:63: unexpected token: + /tmp/mandelbrot.occ:63: expected :=, !, or ? after y[...], got ] /tmp/mandelbrot.occ:63: unexpected token: ] /tmp/mandelbrot.occ:63: unexpected token: : /tmp/mandelbrot.occ:65: unexpected token: INLINE /tmp/mandelbrot.occ:65: unexpected token: FUNCTION /tmp/mandelbrot.occ:65: unexpected token: STRING /tmp/mandelbrot.occ:65: unexpected token: ( /tmp/mandelbrot.occ:65: expected type after VAL, got IDENT /tmp/mandelbrot.occ:65: unexpected token in expression: IS /tmp/mandelbrot.occ:66: expected ], got , /tmp/mandelbrot.occ:66: unexpected token: , /tmp/mandelbrot.occ:67: unexpected token: ( /tmp/mandelbrot.occ:67: expected :=, !, or ? after x[...], got * /tmp/mandelbrot.occ:67: unexpected token: * /tmp/mandelbrot.occ:67: expected :=, !, or ? after y[...], got ) /tmp/mandelbrot.occ:67: unexpected token: ) /tmp/mandelbrot.occ:67: unexpected token: + /tmp/mandelbrot.occ:67: unexpected token: ( /tmp/mandelbrot.occ:67: expected :=, !, or ? after x[...], got * /tmp/mandelbrot.occ:67: unexpected token: * /tmp/mandelbrot.occ:67: expected :=, !, or ? after y[...], got ) /tmp/mandelbrot.occ:67: unexpected token: ) /tmp/mandelbrot.occ:67: unexpected token: ] /tmp/mandelbrot.occ:67: unexpected token: : /tmp/mandelbrot.occ:70: expected type in parameter, got IDENT /tmp/mandelbrot.occ:70: expected ), got IDENT /tmp/mandelbrot.occ:70: unexpected token: ) /tmp/mandelbrot.occ:72: unexpected token: VALOF /tmp/mandelbrot.occ:73: expected type after INITIAL, got IDENT /tmp/mandelbrot.occ:73: unexpected token: IS /tmp/mandelbrot.occ:73: expected ], got ILLEGAL /tmp/mandelbrot.occ:73: unexpected token: ILLEGAL /tmp/mandelbrot.occ:73: unexpected token: INT /tmp/mandelbrot.occ:73: unexpected token: , /tmp/mandelbrot.occ:73: unexpected token: INT /tmp/mandelbrot.occ:73: unexpected token: ILLEGAL /tmp/mandelbrot.occ:73: unexpected token: INT /tmp/mandelbrot.occ:73: unexpected token: ] /tmp/mandelbrot.occ:73: unexpected token: : /tmp/mandelbrot.occ:76: expected ), got ILLEGAL /tmp/mandelbrot.occ:76: expected ), ... [12014 characters truncated] ... EGAL /tmp/mandelbrot.occ:103: unexpected token: INT /tmp/mandelbrot.occ:103: unexpected token: ) /tmp/mandelbrot.occ:103: unexpected token: ) /tmp/mandelbrot.occ:103: unexpected token: - /tmp/mandelbrot.occ:103: expected :=, !, or ? after job[...], got : /tmp/mandelbrot.occ:103: unexpected token: : /tmp/mandelbrot.occ:106: expected ), got ( /tmp/mandelbrot.occ:106: unexpected token: ( /tmp/mandelbrot.occ:106: unexpected token: ) /tmp/mandelbrot.occ:106: unexpected token: ) /tmp/mandelbrot.occ:106: unexpected token: \ /tmp/mandelbrot.occ:106: unexpected token: INT /tmp/mandelbrot.occ:113: unexpected token: : /tmp/mandelbrot.occ:115: expected type in parameter, got IDENT /tmp/mandelbrot.occ:115: expected ), got ? /tmp/mandelbrot.occ:115: unexpected token: ? /tmp/mandelbrot.occ:115: unexpected token: ! /tmp/mandelbrot.occ:115: unexpected token: , /tmp/mandelbrot.occ:115: expected IDENT, got , /tmp/mandelbrot.occ:115: unexpected token: , /tmp/mandelbrot.occ:115: unexpected token in expression: ) /tmp/mandelbrot.occ:116: expected :, got ILLEGAL /tmp/mandelbrot.occ:116: unexpected token: ILLEGAL /tmp/mandelbrot.occ:116: unexpected token: INT /tmp/mandelbrot.occ:116: unexpected token: : /tmp/mandelbrot.occ:117: expected :, got ILLEGAL /tmp/mandelbrot.occ:117: unexpected token: ILLEGAL /tmp/mandelbrot.occ:117: unexpected token: INT /tmp/mandelbrot.occ:117: unexpected token: : /tmp/mandelbrot.occ:118: expected :, got ILLEGAL /tmp/mandelbrot.occ:118: unexpected token: ILLEGAL /tmp/mandelbrot.occ:118: unexpected token: INT /tmp/mandelbrot.occ:118: unexpected token: : /tmp/mandelbrot.occ:119: expected :, got ILLEGAL /tmp/mandelbrot.occ:119: unexpected token: ILLEGAL /tmp/mandelbrot.occ:119: unexpected token: INT /tmp/mandelbrot.occ:119: unexpected token: : /tmp/mandelbrot.occ:120: expected :, got ILLEGAL /tmp/mandelbrot.occ:120: unexpected token: ILLEGAL /tmp/mandelbrot.occ:120: unexpected token: INT /tmp/mandelbrot.occ:120: unexpected token: : /tmp/mandelbrot.occ:143: expected ), got ILLEGAL /tmp/mandelbrot.occ:143: unexpected token: ILLEGAL /tmp/mandelbrot.occ:143: unexpected token: INT /tmp/mandelbrot.occ:143: unexpected token: ) /tmp/mandelbrot.occ:148: expected ), got ILLEGAL /tmp/mandelbrot.occ:148: unexpected token: ILLEGAL /tmp/mandelbrot.occ:148: unexpected token: INT /tmp/mandelbrot.occ:148: unexpected token: ) /tmp/mandelbrot.occ:149: expected ), got ILLEGAL /tmp/mandelbrot.occ:149: unexpected token: ILLEGAL /tmp/mandelbrot.occ:149: unexpected token: INT /tmp/mandelbrot.occ:149: unexpected token: ) /tmp/mandelbrot.occ:153: expected type after INITIAL, got [ /tmp/mandelbrot.occ:153: unexpected token: INT /tmp/mandelbrot.occ:153: unexpected token: ] /tmp/mandelbrot.occ:153: expected ], got ILLEGAL /tmp/mandelbrot.occ:153: expected :, got ILLEGAL /tmp/mandelbrot.occ:153: unexpected token: ILLEGAL /tmp/mandelbrot.occ:153: unexpected token: INT /tmp/mandelbrot.occ:153: unexpected token: ILLEGAL /tmp/mandelbrot.occ:153: unexpected token: INT /tmp/mandelbrot.occ:153: unexpected token: ] /tmp/mandelbrot.occ:153: unexpected token: : /tmp/mandelbrot.occ:158: unexpected token: : /tmp/mandelbrot.occ:160: expected type in parameter, got IDENT /tmp/mandelbrot.occ:160: expected ), got ? /tmp/mandelbrot.occ:160: unexpected token: ? /tmp/mandelbrot.occ:160: unexpected token: ? /tmp/mandelbrot.occ:160: unexpected token: , /tmp/mandelbrot.occ:160: unexpected token in expression: , /tmp/mandelbrot.occ:160: expected IDENT, got , /tmp/mandelbrot.occ:160: unexpected token: , /tmp/mandelbrot.occ:160: expected :, got ! /tmp/mandelbrot.occ:160: unexpected token: ! /tmp/mandelbrot.occ:160: unexpected token: ) /tmp/mandelbrot.occ:161: expected type after array size, got [ /tmp/mandelbrot.occ:161: unexpected token: ] /tmp/mandelbrot.occ:179: expected indented body after ALT case /tmp/mandelbrot.occ:179: expected & after guard in ALT case /tmp/mandelbrot.occ:179: unexpected token: ; /tmp/mandelbrot.occ:179: unexpected token: ; /tmp/mandelbrot.occ:179: expected :=, !, or ? after framebuffer[...], got [ /tmp/mandelbrot.occ:180: expected type after array size, got NEWLINE /tmp/mandelbrot.occ:187: expected IDENT, got AFTER /tmp/mandelbrot.occ:187: unexpected token: AFTER /tmp/mandelbrot.occ:218: unexpected token: ( /tmp/mandelbrot.occ:218: unexpected token: <= /tmp/mandelbrot.occ:218: unexpected token: ( /tmp/mandelbrot.occ:218: unexpected token: + /tmp/mandelbrot.occ:218: unexpected token: INT /tmp/mandelbrot.occ:218: unexpected token: ) /tmp/mandelbrot.occ:218: unexpected token: ) /tmp/mandelbrot.occ:218: unexpected token: & /tmp/mandelbrot.occ:223: unexpected token: : /tmp/mandelbrot.occ:227: unexpected token: : /tmp/mandelbrot.occ:228: unexpected token: : /tmp/mandelbrot.occ:229: unexpected token: : /tmp/mandelbrot.occ:230: unexpected token: :
Now I have a clear picture. Let me analyze the errors systematically and categorize the missing features.
Here's my analysis of the blocking language features for issue #70:
mandelbrot.occDATA TYPE ... RECORD (lines 27-30)DATA TYPE COMPLEX64
RECORD
REAL64 real, imag:
:
The transpiler supports RECORD but not DATA TYPE name RECORD — the named data type form with multi-line field declarations. This is the foundation for COMPLEX64 used throughout the file.
REAL64 INLINE FUNCTION COMPLEX64ABSSQ (VAL COMPLEX64 x) IS ...
INT FUNCTION calc (VAL COMPLEX64 a)
INITIAL COMPLEX64 iter IS [0.0, 0.0]:
The parser doesn't recognize COMPLEX64 as a type in parameter lists, INITIAL declarations, or function return types. Currently only built-in types are accepted.
PROTOCOL P.JOB.IN IS INT; INT; [4]REAL64:
PROTOCOL P.JOB.OUT IS INT; INT; [IMAGE.WIDTH * 2]BYTE:
Protocol field types can't be arrays ([4]REAL64, [expr]BYTE). The parser only expects simple type names.
[MAX.FRAMES][IMAGE.HEIGHT][IMAGE.WIDTH * 2]BYTE framebuffer:
Only 1D arrays are supported; multi-dimensional [n][m][k]TYPE is not.
framebuffer[frame \ MAX.FRAMES][id]
framebuffer[lbuf][j][i*2]
Chained [][] index access isn't parsed.
CHAN TYPE / MOBILE RECORD / SHARED / CLAIM / MOBILE allocation (lines 48-57, 83, 93-94, 111, 227-233)CHAN TYPE CT.WORK.IN
MOBILE RECORD
CHAN INT notify?: ...
SHARED CT.WORK.IN! mwcli.in:
CLAIM link.in
mwsvr.in, mwcli.in := MOBILE CT.WORK.IN
Mobile channel types, shared channel ends, CLAIM blocks, and MOBILE allocation are all unimplemented. This is the biggest feature gap — it's the concurrency abstraction used for the worker pool.
COMPLEX64 INLINE FUNCTION "+" (VAL COMPLEX64 x, y) IS ...
COMPLEX64 INLINE FUNCTION "**" (VAL COMPLEX64 x, y) IS ...
Functions with string-literal names that overload operators for user-defined types.
VAL [][n]TYPE (line 43)VAL [][5]BYTE ansicolours IS ["*#1B[31m", ...]:
2D open array abbreviation (array of fixed-size arrays).
[x,y](TYPE) (line 106)val := calc ([x,y](COMPLEX64)) \ 16
Array literal with an explicit type cast suffix (COMPLEX64).
[i = 0 FOR n | expr] (line 153)INITIAL [4]REAL64 wpacket IS [i = 0 FOR 4 | 0.0]:
Replicator-based array construction.
INITIAL with array types (line 153)INITIAL [4]REAL64 wpacket IS [i = 0 FOR 4 | 0.0]:
INITIAL currently only accepts simple types, not [n]TYPE.
RESCHEDULE() intrinsic (line 109)Minor — maps to runtime.Gosched().
tim ? AFTER t in ALT guards (line 187)tim ? AFTER t
Timer-with-AFTER as an ALT input guard. Regular AFTER in timer reads may work, but not in ALT cases.
VAL REAL64 x.target IS 1.16000014859:
VAL REAL64 y.target IS -0.27140215303:
These trigger ILLEGAL token errors, suggesting the lexer isn't handling longer float literals correctly.
(hframe <= (lframe + 4)) & tick ? hframe
Boolean expression as an ALT pre-condition guard. The parser seems to not handle parenthesized boolean guards.
| Priority | Feature | Lines affected |
|---|---|---|
| Critical | CHAN TYPE / MOBILE / SHARED / CLAIM |
48-57, 83, 93, 111, 227-233 |
| Critical | DATA TYPE RECORD + user-defined types in params |
27-30, 59, 62-67, 70, 73, 106 |
| High | Multi-dimensional arrays (decl + indexing) | 43, 161, 179, 199, 207 |
| High | Array types in protocol fields | 45-46 |
| Medium | Operator overloading (string function names) | 62-67 |
| Medium | INITIAL with array types |
153 |
| Medium | Array comprehensions [i = 0 FOR n \| expr] |
153 |
| Medium | Typed array constructor [x,y](TYPE) |
106 |
| Low | tim ? AFTER t in ALT |
187 |
| Low | Boolean ALT pre-condition guards | 218 |
| Low | RESCHEDULE() |
109 |
| Low | REAL64 long float literal lexing | 116-120, 143, 148-149 |
The CHAN TYPE/MOBILE/SHARED/CLAIM family is the single biggest blocker — it's fundamental to this program's architecture. DATA TYPE RECORD with user-defined types is the second largest, as COMPLEX64 pervades the computation. Want me to post this to the issue?
Yes please post your findings in the issue. Also if you could look at other open issues to determine which ones relate to the missing features you've identified. Use that information to add references in your comment on issue 70 to the dependent bugs. That way someone in the future can easily identify which issues have been fixed and so on.
70 OPEN Investigate which language features are blocking mandelbrot example 2026-02-23T15:16:51Z 67 OPEN Support guarded ALT with boolean pre-conditions 2026-02-23T04:07:07Z 66 OPEN Support PRI ALT (prioritised alternation) 2026-02-23T04:07:06Z 65 OPEN Support *" string escape sequence 2026-02-23T04:07:05Z 64 OPEN Support multi-dimensional arrays ([][n]TYPE) 2026-02-23T04:06:39Z 63 OPEN Support SIZE of direction-annotated channel name 2026-02-23T04:06:33Z 62 OPEN Support expression-sized array declarations 2026-02-23T04:06:31Z 61 OPEN Support multi-value CASE labels 2026-02-23T04:06:23Z 60 OPEN Support timer AFTER input (tim ? AFTER expr) 2026-02-23T04:06:19Z 43 OPEN Course examples: unimplemented features preventing transpilation 2026-02-23T04:06:05Z 40 OPEN Support KRoC mobile/shared channel extensions 2026-02-22T16:52:58Z
title: Support KRoC mobile/shared channel extensions
state: OPEN
author: associate-1
labels:
comments: 0
assignees:
projects:
milestone:
number: 40
--
## Summary
Several advanced KRoC (Kent Retargetable occam Compiler) extensions are not yet supported. These are needed by programs like the mandelbrot demo (`kroc/demos/ttygames/mandelbrot.occ`, see #39) and likely other KRoC programs that use mobile data, typed channels, and shared-resource synchronization.
## Unsupported Constructs
### 1. `DATA TYPE` — Named record type alias
```occam
DATA TYPE COMPLEX64
RECORD
REAL64 real, imag:
:
```
### 2. `CHAN TYPE` — Channel type definitions with mobile record payloads
```occam
CHAN TYPE CT.WORK.IN
MOBILE RECORD
CHAN INT notify?:
CHAN P.JOB.IN in!:
:
```
### 3. `INLINE FUNCTION` — Inline modifier on function declarations
```occam
REAL64 INLINE FUNCTION COMPLEX64ABSSQ (VAL COMPLEX64 x) IS
(x[real]*x[real]) + (x[imag]*x[imag]):
```
### 4. `SHARED` parameter qualifier — Shared channel ownership
```occam
PROC worker (SHARED CT.WORK.IN! link.in, SHARED CT.WORK.OUT! link.out)
```
### 5. `MOBILE` constructor — Creating mobile channel type instances
```occam
mwsvr.in, mwcli.in := MOBILE CT.WORK.IN
```
### 6. `CLAIM` block — Mutual exclusion for shared channel access
```occam
CLAIM link.in
link.in[in] ? frame; jobid; job
```
## Notes
These are all KRoC-specific extensions beyond base occam for mobile data, typed channels, and shared-resource synchronization. Each would require new lexer tokens, AST nodes, parser rules, and codegen support.
Suggested implementation priority (simplest first):
1. **INLINE FUNCTION** — Modify FuncDecl parsing to accept and ignore the INLINE keyword
2. **DATA TYPE** — New TypeAlias AST node and record body parsing
3. **SHARED qualifier** — Add to parameter parsing for channels
4. **CLAIM block** — New statement type for synchronization (could map to `sync.Mutex`)
5. **CHAN TYPE / MOBILE** — Most complex; requires new type definitions and constructor expressions
Related: #39title: Course examples: unimplemented features preventing transpilation state: OPEN author: associate-1 labels: comments: 3 assignees: projects: milestone: number: 43 -- ## Summary Transpiling the 16 example programs in `kroc/modules/course/examples/` reveals that 11 out of 16 fail to parse due to unimplemented language features. Only `hello_world.occ`, `hello_seq_world.occ`, `hello_raw_world.occ`, `echoing.occ`, and `double_echo.occ` transpile successfully. This is a tracking issue for the full set of missing features discovered. Some have their own dedicated issues. ## Unimplemented Features ### Standard occam features | Feature | Description | Affected files | Issue | |---------|-------------|----------------|-------| | `INLINE FUNCTION` | Function qualifier `INT INLINE FUNCTION f(...)` | `bar.occ` | #44 | | `ALT` replicators | `ALT i = 0 FOR n` (like SEQ/PAR/IF replicators) | `sort_pump.occ`, `sort_pump_2.occ`, `sort_inside.occ`, `sort_inside_2.occ`, `bar.occ` | #45 | | `INT16`/`INT32`/`INT64` | Extended integer width types | `cast.occ`, `casting.occ`, `commstime.occ` | #46 | | `tim ? AFTER expr` | Timer AFTER input (wait until time) | `bar.occ`, `demo.occ`, `sort_inside_2.occ` | | | `PRI ALT` | Prioritised alternation | `bar.occ` | | | Guarded ALT (`&`) | `(condition) & chan ? x` boolean pre-conditions | `bar.occ` | | | Multi-value `CASE` labels | `'*n', '*c'` comma-separated case values | `test_bell.occ` | | | `[][]TYPE` / `[][n]TYPE` | Multi-dimensional array declarations | `bar.occ`, `test_utils.occ` | | | Dotted protocol tags | `bar.blank`, `bar.terminate` in PROTOCOL CASE | `bar.occ` | | | `*"` string escape | Escaped quote character inside string literals | `test_utils.occ` | | ### occam-pi extensions | Feature | Description | Affected files | Issue | |---------|-------------|----------------|-------| | `SHARED CHAN` / `CLAIM` | Shared channels and exclusive access blocks | `bar.occ` | #40 | | `MOBILE` types | Dynamically allocated mobile data | `demo.occ` | #40 | ## Error distribution by file | File | Root causes | |------|-------------| | `bar.occ` | INLINE, PRI ALT, ALT replicator, AFTER, SHARED/CLAIM, guarded ALT, multi-dim arrays, dotted protocol tags | | `cast.occ` | INT16/INT32/INT64 | | `casting.occ` | INT16/INT32/INT64 | | `commstime.occ` | INT64 | | `demo.occ` | AFTER, MOBILE | | `sort_inside.occ` | ALT replicator, multi-dim arrays | | `sort_inside_2.occ` | ALT replicator, AFTER | | `sort_pump.occ` | ALT replicator | | `sort_pump_2.occ` | ALT replicator | | `test_bell.occ` | Multi-value CASE labels | | `test_utils.occ` | Multi-dim arrays, string escape |
title: Support timer AFTER input (tim ? AFTER expr) state: OPEN author: associate-1 labels: comments: 0 assignees: projects: milestone: number: 60 -- **Difficulty: Medium** — Parser, AST, and codegen changes across timer input handling. ## Summary Occam's `tim ? AFTER expr` construct waits until the timer value exceeds `expr`. This is used for timed delays and is distinct from `tim ? t` (which just reads the current time). ## Occam syntax ```occam TIMER tim: INT t: SEQ tim ? t -- read current time tim ? AFTER t PLUS delay -- wait until time passes t + delay ``` ## Proposed Go mapping ```go t = time.Now().UnixMicro() time.Sleep(time.Duration(t + delay - time.Now().UnixMicro()) * time.Microsecond) ``` Or possibly using `AFTER` comparison semantics (modular time comparison). ## Affected files - `bar.occ` (lines 176, 189, 225, 373) - `demo.occ` (line 16) - `sort_inside_2.occ` (line 323) ## Work required 1. **Parser**: Detect `AFTER` keyword after `?` in channel/timer input and parse the delay expression 2. **AST**: Add an `AfterExpr` field (or similar) to the input node 3. **Codegen**: Generate `time.Sleep(...)` or equivalent Part of #43.
title: Support expression-sized array declarations state: OPEN author: associate-1 labels: comments: 0 assignees: projects: milestone: number: 62 -- **Difficulty: Medium** — Parser must accept expressions as array sizes; codegen must emit `make()` for non-literal sizes since Go arrays require compile-time constants. ## Summary Occam allows array sizes to be arbitrary expressions (typically referencing variables or constants), not just integer literals. Currently the parser only accepts literal integers as array sizes. ## Occam syntax ```occam VAL INT max.delay IS 100: [max.delay]BYTE buffer: [total - 1]BYTE temp: ``` ## Proposed Go mapping ```go maxDelay := 100 buffer := make([]byte, maxDelay) temp := make([]byte, total - 1) ``` Since Go arrays require compile-time constant sizes, expression-sized arrays should map to slices via `make()`. ## Affected files - `sort_inside.occ` (lines 309, 333, 336) ## Work required 1. **Parser**: Parse the array size as a full expression instead of requiring an integer literal 2. **Codegen**: When array size is not a literal, generate `make([]type, expr)` instead of a fixed-size array Part of #43.
title: Support multi-dimensional arrays ([][n]TYPE)
state: OPEN
author: associate-1
labels:
comments: 0
assignees:
projects:
milestone:
number: 64
--
**Difficulty: Medium** — Parser, AST, and codegen must all support nested array dimensions.
## Summary
Occam supports multi-dimensional arrays such as `[][2]BYTE` (open first dimension, fixed second) and `[n][m]TYPE` (both fixed). These are currently not parsed.
## Occam syntax
```occam
VAL [][2]BYTE pairs IS [['A', 'a'], ['B', 'b']]:
[10][20]INT grid:
```
## Proposed Go mapping
```go
var pairs = [][2]byte{{'A', 'a'}, {'B', 'b'}}
var grid [10][20]int
```
Or using slices for open dimensions:
```go
var pairs [][]byte = [][]byte{{'A', 'a'}, {'B', 'b'}}
```
## Affected files
- `bar.occ` (multi-dim array params/declarations)
- `test_utils.occ` (line 19: `VAL [][2]BYTE`)
## Work required
1. **Parser**: After parsing `[n]`, check for another `[` to handle nested array dimensions
2. **AST**: Extend array type representation to support multiple dimensions
3. **Codegen**: Generate nested Go array/slice types
Part of #43.title: Support *" string escape sequence
state: OPEN
author: associate-1
labels:
comments: 0
assignees:
projects:
milestone:
number: 65
--
**Difficulty: Easy** — Small addition to existing escape sequence handling in the lexer.
## Summary
Occam uses `*"` inside string literals to represent a literal double-quote character. Currently the lexer does not handle this escape sequence, causing parse failures.
## Occam syntax
```occam
VAL []BYTE msg IS "He said *"hello*"":
```
## Proposed Go mapping
```go
var msg []byte = []byte("He said \"hello\"")
```
## Affected files
- `test_utils.occ` (line 75)
## Work required
1. **Lexer**: In string literal scanning, recognize `*"` as an escape sequence that produces a `"` character (rather than terminating the string)
This is a small addition to the existing occam escape sequence handling (`*n`, `*c`, `*t`, etc.).
Part of #43.title: Support guarded ALT with boolean pre-conditions
state: OPEN
author: associate-1
labels:
comments: 0
assignees:
projects:
milestone:
number: 67
--
**Difficulty: Medium** — Parser must handle `expr &` prefix on ALT cases; codegen must conditionally enable/disable select cases (e.g., by setting channels to `nil` when the guard is false).
## Summary
Occam ALT cases can have boolean guard pre-conditions using the `&` operator. A guard that evaluates to FALSE disables that ALT branch. Currently the parser does not support this syntax.
## Occam syntax
```occam
ALT
running & in ? x
... only considered if running is TRUE
(NOT paused) & tim ? AFTER timeout
... only considered if paused is FALSE
TRUE & stop ? any
... always considered
```
## Proposed Go mapping
```go
select {
case x = <-in: // wrap in: if running { ... }
case <-time.After(...): // wrap in: if !paused { ... }
case any = <-stop: // always active
}
```
For `reflect.Select`-based ALT replicators, disabled guards can be handled by setting the channel to `nil` (which makes a select case block forever, effectively disabling it).
## Affected files
- `bar.occ` (lines 327, 338, 352, 399, 402)
## Work required
1. **Parser**: Before parsing a channel input in ALT, check for `expr &` guard prefix
2. **AST**: Add a `Guard` expression field to ALT case nodes
3. **Codegen**: Conditionally enable/disable select cases based on guard value
Part of #43.Now I have all the information I need. Let me also check issues #61 and #66 to see if they're relevant.
gh issue comment 70 --body "$(cat <<'EOF' ## Investigation Results The mandelbrot demo (`kroc/demos/ttygames/mandelbrot.occ`) exercises many advanced occam and occam-pi features. Below is a categorized list of every missing feature blocking clean transpilation, with references to existing issues where applicable. ### Blocking Features #### Critical — Fundamental to program architecture | Feature | Lines | Existing Issue | Notes | |---------|-------|----------------|-------| | `CHAN TYPE` / `MOBILE RECORD` definitions | 48–57 | #40 | Defines typed channel bundles `CT.WORK.IN`, `CT.WORK.OUT` | | `SHARED` channel qualifier | 83, 229–230 | #40 | `SHARED CT.WORK.IN! mwcli.in` for worker pool | | `CLAIM` blocks | 93–94, 111–112 | #40 | Mutual exclusion for shared channel access | | `MOBILE` allocation expressions | 232–233 | #40 | `mwsvr.in, mwcli.in := MOBILE CT.WORK.IN` | | `DATA TYPE ... RECORD` (named record types) | 27–30 | #40 | `DATA TYPE COMPLEX64 RECORD ...` — foundation for all complex arithmetic | | User-defined types in params/decls | 59, 62, 65, 70, 73 | — | `VAL COMPLEX64 x` as function parameter type; `INITIAL COMPLEX64 iter IS ...` | #### High — Multiple occurrences, significant code sections | Feature | Lines | Existing Issue | Notes | |---------|-------|----------------|-------| | Multi-dimensional array declarations | 43, 161 | #64 | `[][5]BYTE`, `[n][m][k]BYTE` | | Multi-dimensional array indexing | 179, 199, 207 | #64 | `framebuffer[a][b][c]` chained indexing | | Array types in protocol fields | 45–46 | — | `PROTOCOL P.JOB.IN IS INT; INT; [4]REAL64:` | | Expression-sized array declarations | 46, 90, 161 | #62 | `[IMAGE.WIDTH * 2]BYTE`, `[MAX.FRAMES][...]BYTE` | | Operator overloading (string function names) | 62–67 | — | `COMPLEX64 INLINE FUNCTION "+" (...)`, `"**"` | #### Medium — Isolated occurrences | Feature | Lines | Existing Issue | Notes | |---------|-------|----------------|-------| | `INITIAL` with array types | 153 | — | `INITIAL [4]REAL64 wpacket IS ...` | | Array comprehensions `[i = 0 FOR n \| expr]` | 153 | — | `[i = 0 FOR 4 \| 0.0]` replicator-based construction | | Typed array literal constructor `[x,y](TYPE)` | 106 | — | `[x,y](COMPLEX64)` explicit type on array literal | | `*"` string escape | 84 | #65 | `'*"'` inside the `table` byte literal | | `CHAN OF PROTOCOL` params with user-defined protocol types | 94, 112, 115, 140, 154, 160 | — | `CHAN P.JOB.IN in!` — channel of named protocol, used in channel type bodies and proc params | #### Low — Simple additions | Feature | Lines | Existing Issue | Notes | |---------|-------|----------------|-------| | `tim ? AFTER t` (timer AFTER input) | 187 | #60 | Wait-until-time in ALT body | | Guarded ALT with boolean pre-conditions | 218 | #67 | `(hframe <= (lframe + 4)) & tick ? hframe` | | `RESCHEDULE()` intrinsic | 109 | — | Maps to `runtime.Gosched()` | | REAL64 long floating-point literals | 116–120, 143, 148–149 | — | `1.16000014859` triggers ILLEGAL tokens — possible lexer limitation on float length | ### Related Issues Summary - **#40** — `CHAN TYPE` / `MOBILE` / `SHARED` / `CLAIM` / `DATA TYPE` — covers the biggest blockers - **#60** — `tim ? AFTER expr` — timer delay input - **#62** — Expression-sized array declarations - **#64** — Multi-dimensional arrays (declarations and indexing) - **#65** — `*"` string escape sequence - **#67** — Guarded ALT with boolean pre-conditions ### Features with no existing issue These would need new issues if someone wants to track them individually: 1. **User-defined types in params/declarations** — accepting `COMPLEX64` etc. as types in function params, `INITIAL`, variable decls 2. **Array types in protocol fields** — `[n]TYPE` inside `PROTOCOL ... IS` definitions 3. **Operator overloading** — `FUNCTION "+"`, `FUNCTION "**"` with string-literal names 4. **`INITIAL` with array types** — `INITIAL [n]TYPE name IS expr:` 5. **Array comprehensions** — `[i = 0 FOR n | expr]` 6. **Typed array literal constructor** — `[exprs](TYPE)` suffix 7. **`RESCHEDULE()`** — trivial; maps to `runtime.Gosched()` 8. **`CHAN OF PROTOCOL` params** — channels carrying named protocol types 9. **REAL64 long float literal lexing** — possible lexer bug with longer decimal sequences EOF )"
Posted the analysis to issue #70. The comment includes:
The single biggest blocker is #40 — the CHAN TYPE/MOBILE/SHARED/CLAIM family covers the program's entire concurrency architecture. The second largest is user-defined types (DATA TYPE COMPLEX64) which also falls partly under #40.
Thank you for updating the issue. Do you think it would be possible to re-write this Mandlebrot example program such that it only used features implemented in the current version of the transpiler?
The user is asking whether the mandelbrot.occ program could be rewritten to only use features that the current transpiler supports. Let me think about this carefully.
Looking at the mandelbrot program, the key things it does:
COMPLEX64 data type - Used for complex number arithmetic (mandelbrot computation). This could be replaced by using two separate REAL64 variables (real and imag parts) instead of a record type.
Operator overloading ("+" and "")** - These are convenience functions for complex arithmetic. Could be replaced with explicit function calls that take separate real/imag components.
Channel-based worker pool - The trickiest part is the shared channel architecture where multiple workers read from the same channels. Without SHARED and CLAIM, I'd need to restructure this using individual channels per worker or a different distribution pattern, since standard occam channels enforce one-to-one communication.
Multi-dimensional arrays and array types - I could flatten the framebuffer into a single dimension with manual index calculations, and replace protocol arrays with sequential fields or precomputed size constants.
Array initialization - Expression-sized arrays and array comprehensions could be replaced with explicit loops or pre-calculated constants.
Language features to simplify - RESCHEDULE() can be dropped entirely, timer operations could be replaced with basic delay calculations, and guarded ALT conditions might need restructuring. String escapes and long REAL64 literals could be worked around, and user-defined types like COMPLEX64 could be replaced with simple pairs.
Array initialization - Instead of INITIAL with array types, I can use standard array declarations followed by sequential initialization.
The real constraint - The trickiest part is the worker pool pattern that relies on CHAN TYPE and MOBILE extensions. But looking at what's actually supported, I could use a simpler architecture with one job channel per worker and have the harvester read results using ALT replicators, which are available in standard occam.
Protocol flattening - For the input protocol, instead of sending an array of 4 REAL64 values, I can flatten it into four separate sequential fields since sequential protocols are supported. But the output protocol is problematic - sending 200 bytes as an array through a sequential protocol won't work without array support in protocols.
Alternative architecture using shared memory - Rather than passing arrays through channels, I could have workers write directly into a shared framebuffer indexed by their worker ID and signal completion through simple INT channels. This sidesteps the protocol limitations entirely.
Simplified design - The core flow is: farmer generates jobs with frame number, row ID, and the four real64 parameters; workers compute mandelbrot values and write results into shared memory; harvester collects and renders. This avoids needing advanced protocol features altogether.
The real constraint - occam doesn't support SHARED memory easily, so the harvester would need to own the framebuffer and receive results back through channels instead. But sending [200]BYTE arrays through protocols is the blocker.
Possible workarounds - I could send individual pixel values one at a time, give each worker its own output channel for bytes, or simplify to a single-threaded version. But stepping back, the user is really asking about feasibility, not implementation details.
The honest answer - Yes, it's possible with restructuring: handle complex arithmetic through pairs of REAL64 variables and explicit functions, redesign the worker communication to avoid array types in protocols, and accept that the architecture will be more verbose than modern languages allow.
The hardest obstacles - The 3D framebuffer would need flattening, passing array results through channels requires either individual value transmission or a completely different pattern, and guarded ALT statements need control flow restructuring.
Feasibility assessment - It's doable but demands significant refactoring; the resulting program would be functionally correct but less idiomatic occam.
Good question. Let me think through each blocker and whether it can be worked around with currently-supported features.
Easy to work around:
COMPLEX64 data type — Replace with pairs of REAL64 variables. Inline the complex arithmetic (multiply, add, abs-squared) instead of using operator-overloaded functions."+", "**") — Goes away once COMPLEX64 is decomposed into separate real/imag variables.[IMAGE.WIDTH * 2]BYTE) — Precompute as VAL INT constants (e.g., VAL INT OUTPUT.WIDTH IS 200:).INITIAL with array types — Use plain array declaration + SEQ init.RESCHEDULE() — Just remove it; it's a scheduling yield hint.*" escape — Avoid the quote character in the lookup table (replace with a different character or use a byte literal).Moderate restructuring:
[MAX.FRAMES][IMAGE.HEIGHT][WIDTH]BYTE into a 1D array with manual index calculation (buf[(frame * HEIGHT * WIDTH) + (row * WIDTH) + col]). Ugly but functional.tim ? AFTER t — Replace with tim ? now followed by a WHILE loop polling the timer (busy-wait), or just use a fixed delay via a replicator counting loop. Not ideal but functional.(hframe <= (lframe + 4)) & tick ? hframe case is tricky. One workaround: restructure into nested IF/ALT where the boolean check happens before entering the ALT, or use a separate control channel.Major restructuring required:
CHAN TYPE / MOBILE / SHARED / CLAIM — This is the core of the worker-pool architecture. The whole point of SHARED is that multiple workers can read from one channel with mutual exclusion. Without it, you'd need to redesign as:[N.WORKERS]CHAN OF ... with round-robin dispatch from the farmer[4]REAL64 in a protocol could be replaced by sending 4 individual REAL64 values in a sequential protocol (INT; INT; REAL64; REAL64; REAL64; REAL64). The output row ([200]BYTE) is harder — you could send it as individual bytes in a loop, or restructure so workers write into a shared-scope array and just signal completion via a simple INT channel.Verdict: Yes, it's feasible. The hardest part is replacing the SHARED/CLAIM worker pool with per-worker channels, and avoiding array-typed protocol fields for the output row. The cleanest approach would probably be:
ALT i = 0 FOR N.WORKERS to receive from any worker's output channelINT/REAL64 values in a sequential protocolThe result would be less idiomatic occam but functionally equivalent. Want me to write it?
Let's hold that thought for now. Can you add this analysis and proposed plan to the bug report as a comment please?
gh issue comment 70 --body "$(cat <<'EOF' ## Feasibility: Rewriting with Currently-Supported Features It should be possible to rewrite the mandelbrot example using only features the transpiler already supports. Here's an analysis of each blocker and the proposed workaround. ### Easy workarounds | Blocker | Workaround | |---------|------------| | `DATA TYPE COMPLEX64` / user-defined types | Decompose into pairs of `REAL64` variables (`real`, `imag`). Inline the arithmetic. | | Operator overloading (`"+"`, `"**"`) | Goes away once COMPLEX64 is eliminated — just use explicit real/imag math. | | Expression-sized arrays (`[IMAGE.WIDTH * 2]BYTE`) | Precompute as `VAL INT` constants (e.g., `VAL INT OUTPUT.WIDTH IS 200:`). | | Array comprehensions (`[i = 0 FOR 4 \| 0.0]`) | Replace with plain array declaration + SEQ initialization loop. | | Typed array constructor (`[x,y](COMPLEX64)`) | Not needed once COMPLEX64 is eliminated. | | `INITIAL` with array types | Use plain array declaration + SEQ init. | | `RESCHEDULE()` | Remove — it's just a scheduling yield hint. | | `*"` string escape (#65) | Avoid the quote character in the lookup table, or use a byte literal. | | Long REAL64 literals | May be a cascading parse error rather than a real lexer bug. If real, approximate with fewer decimal digits. | ### Moderate restructuring | Blocker | Workaround | |---------|------------| | Multi-dimensional arrays (#64) | Flatten `[MAX.FRAMES][IMAGE.HEIGHT][WIDTH]BYTE` into a 1D array with manual index calculation: `buf[(frame * HEIGHT * WIDTH) + (row * WIDTH) + col]`. | | `tim ? AFTER t` (#60) | Replace with `tim ? now` + a polling WHILE loop, or a fixed delay via a counting loop. | | Guarded ALT (#67) | Restructure `(expr) & chan ? x` into nested IF/ALT where the boolean check happens before entering the ALT, or use a separate control channel. | ### Major restructuring — Worker pool architecture The `CHAN TYPE` / `MOBILE` / `SHARED` / `CLAIM` family (#40) is the biggest blocker. The entire worker-pool pattern needs to be redesigned: **Current architecture** (requires SHARED/CLAIM): - Single shared channel bundle — multiple workers CLAIM and read from it - Farmer pushes jobs onto the shared input channel - Harvester reads from the shared output channel **Proposed replacement** (uses only supported features): - **Per-worker channel pairs**: `[N.WORKERS]CHAN OF P.JOB.IN` and `[N.WORKERS]CHAN OF P.JOB.OUT` - **Farmer** dispatches round-robin to individual worker input channels - **Harvester** uses `ALT i = 0 FOR N.WORKERS` (supported) to receive from any worker's output channel - Eliminates the need for SHARED, CLAIM, CHAN TYPE, and MOBILE entirely **Array types in protocol fields** — also needs workaround: - Job params (`[4]REAL64`): send as 6 individual values in a sequential protocol — `PROTOCOL P.JOB.IN IS INT; INT; REAL64; REAL64; REAL64; REAL64:` - Output row (`[200]BYTE`): hardest part. Options: (a) workers send individual bytes in a loop over a plain `CHAN OF BYTE`, or (b) restructure so workers write to a higher-scoped array and signal completion via a simple `CHAN OF INT` ### Proposed rewrite plan 1. Eliminate `COMPLEX64` — use pairs of REAL64 variables for all complex arithmetic 2. Replace shared channel bundles with per-worker `[N.WORKERS]CHAN OF ...` arrays 3. Farmer dispatches jobs round-robin; harvester uses ALT replicator to collect results 4. Flatten the 3D framebuffer to 1D with manual index math 5. Replace sequential protocol array fields with individual values 6. Replace `tim ? AFTER` with timer polling or a delay loop 7. Remove `RESCHEDULE()`, work around `*"` escape and guarded ALT The result would be less idiomatic occam but functionally equivalent and transpilable today. EOF )"
Done — posted the feasibility analysis and rewrite plan as a second comment on issue #70.