title: Support PRI ALT (prioritised alternation)
state: OPEN
author: associate-1
labels:
comments: 1
assignees:
projects:
milestone:
number: 66
--
**Difficulty: Medium** — New keyword, AST flag, and codegen strategy needed. Go `select` has no native priority semantics, so a faithful implementation requires nested selects with `default` clauses or a similar workaround.
## Summary
`PRI ALT` is a prioritised version of `ALT` where earlier guards have priority over later ones when multiple are ready simultaneously. Currently only `ALT` is supported.
## Occam syntax
```occam
PRI ALT
in ? x
... handle input
tim ? AFTER timeout
... handle timeout
```
## Proposed Go mapping
Go's `select` does not have built-in priority. Options:
1. **Simple approach**: Map `PRI ALT` to `select` (same as `ALT`), accepting the loss of priority semantics. Add a comment noting the limitation.
2. **Faithful approach**: Use nested `select` with `default` clauses to check higher-priority channels first.
## Affected files
- `bar.occ` (multiple PRI ALT blocks)
## Work required
1. **Lexer**: Recognize `PRI` keyword (if not already)
2. **Parser**: Parse `PRI ALT` as a variant of ALT
3. **AST**: Add a `Priority` flag to the ALT node
4. **Codegen**: Generate appropriate Go select construct
Part of #43.author: dboreham association: member edited: false status: none -- Has this already been fixed? I think there was a recent PR that implemented the simple version, and we have another bug open to track the "full fat" implementation: #84 --
{
"pattern": "PRI ALT|PRI_ALT|PriAlt|Priority.*Alt",
"output_mode": "content",
"head_limit": 20
}TEST_COVERAGE_IMPROVEMENTS.md:154:- PRI ALT / PRI PAR (not yet implemented)
parser/parser_test.go:384:func TestPriAltBlock(t *testing.T) {
parser/parser_test.go:385: input := `PRI ALT
parser/parser_test.go:406: t.Error("expected Priority to be true for PRI ALT")
CLAUDE.md:72:| `ALT` / `PRI ALT` | `select` |
CLAUDE.md:194:[Omitted long matching line]
README.md:538:4. **ALT construct**: Occam's `ALT` maps to Go's `select` statement. Basic ALT, guards, and timer timeouts are supported. Priority ALT (`PRI ALT`) and replicated ALT are not yet implemented.
TODO.md:11:- **ALT / PRI 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`). PRI ALT treated identically (Go has no priority select).
TODO.md:98:| ~~**PRI ALT / PRI PAR**~~ | ~~Priority variants of ALT and PAR.~~ **Implemented** — treated as ALT/PAR (Go has no priority select). |
codegen/e2e_concurrency_test.go:357:func TestE2E_PriAlt(t *testing.T) {
codegen/e2e_concurrency_test.go:358: // Test PRI ALT: behaves the same as ALT in Go (no priority semantics)
codegen/e2e_concurrency_test.go:365: PRI ALT
ast/ast.go:392: Priority bool // true for PRI ALT
[Showing results with pagination = limit: 20, offset: 0]This issue is already implemented. As the comment from dboreham suggests, PRI ALT has been done with the "simple approach" — it's parsed, has a Priority flag in the AST (ast/ast.go:392), and generates the same select as regular ALT (since Go has no native priority select). There are both parser tests (parser/parser_test.go:384) and e2e tests (codegen/e2e_concurrency_test.go:357).
The TODO.md already marks it as Implemented. Issue #84 tracks the potential future "faithful" implementation with nested selects.
This issue can be closed. Want me to close it with a comment?
gh issue close 66 -c "Closing as already implemented. PRI ALT is parsed, has a Priority flag in the AST, and generates Go \`select\` (same as ALT, since Go has no native priority select). Parser tests and e2e tests are in place. Issue #84 tracks a potential future faithful implementation with nested selects."
title: Include modified life Occam example back in repository state: OPEN author: dboreham labels: comments: 0 assignees: projects: milestone: number: 97 -- We have an Occam example program taken from the book "Programming in Occam2" that we are currently hosting via a script that downloads the program from the book web site then modifies it locally. This work was done under https://github.com/codeassociates/occam2go/issues/94 We now have permission from the copyright holder to host the actual file in our repository. Therefore the file (with our edits) can be committed directly in our repository, as it was before. The download/edit script can be removed since it will no longer be needed.
{
"subagent_type": "Explore",
"prompt": "Find all files related to the \"life\" occam example program in this repo. Look for:\n1. The download/patch script mentioned in issue #94\n2. Any life.occ files (including the untracked one in git status)\n3. The historical-examples directory structure\n4. Any references to \"life\" in Makefiles, READMEs, test files, etc.\n\nGive me file paths and brief descriptions of what each contains.",
"model": "haiku"
}Now I have all the information. Let me compile the findings: ## Summary of "Life" Example Program Files Here are all the files related to the "life" occam example program: ### 1. Download/Patch Script **File:** `/home/david/projects/codeassociates/occam2go/historical-examples/fetch-life.sh` - Executable bash script that downloads the original Life source from the "Programming in occam 2" book website - Applies sed-based modifications to adapt the copyrighted source for occam2go - Changes include: adding copyright header, adding helper procedures (write.string, write.small.int), setting board dimensions to 20x20, replacing ANSI terminal functions, adding channel direction annotations, wrapping main body in PROC declaration - Downloads from: https://www.cs.ox.ac.uk/geraint.jones/publications/book/Pio2/code-life.txt ### 2. Generated Life Source Code **File:** `/home/david/projects/codeassociates/occam2go/historical-examples/life.occ` - The modified occam source code after running the fetch script - Contains Conway's Game of Life implementation - Includes copyright attribution to Geraint Jones & Michael Goldsmith (1988, 2001) - Also included in .gitignore (entry: `historical-examples/life.occ`) since it's generated rather than checked in ### 3. Transpiled Go Code **File:** `/home/david/projects/codeassociates/occam2go/life.go` - The Go output produced by transpiling life.occ - Currently untracked in git (shown in git status) - Contains Go implementations of the life game with protocol types, channels, and goroutines - Package main with buffer I/O, terminal control, and sync constructs ### 4. Documentation **File:** `/home/david/projects/codeassociates/occam2go/historical-examples/README.md` - Explains the historical examples directory - Documents the Life example from "Programming in occam 2" by Jones & Goldsmith (1988) - Points users to run `./historical-examples/fetch-life.sh` to generate life.occ - Provides usage instructions: transpile with `./occam2go -o life.go historical-examples/life.occ` and run with `go run life.go` - Lists game controls (E=editor, R=run, S=stop, space/asterisk=toggle cells, Q=quit) ### 5. Git References - Multiple commits in the history related to life: - `cf76024` (2024-02-24): "Replace copyrighted life.occ with download-and-patch script (#94)" - the main refactoring to use the fetch script - `baa42a2`: Merge PR #95 for the above change - Several earlier commits (`eb7faab`, `5ab69cb`, `126b182`, etc.) from fixing compilation and runtime issues All "life" files are organized in the `historical-examples/` subdirectory except for the top-level `life.go` which is the transpiled output.
agentId: a4a08eab176d31fb2 (for resuming to continue this agent's work if needed) <usage>total_tokens: 24752 tool_uses: 15 duration_ms: 54127</usage>
{
"file_path": "/home/david/projects/codeassociates/occam2go/historical-examples/fetch-life.sh"
} 1→#!/bin/bash
2→#
3→# Downloads the Life example from the "Programming in occam 2" book website
4→# and applies modifications needed to build with occam2go and the kroc course module.
5→#
6→# The original source is copyright Geraint Jones & Michael Goldsmith 1988, 2001.
7→# See: https://www.cs.ox.ac.uk/geraint.jones/publications/book/Pio2/code-life.txt
8→#
9→# Changes applied:
10→# - Add copyright/attribution header and adaptation notes
11→# - Add helper procedures (write.string, write.small.int) replacing book library
12→# - Set board dimensions (array.width/array.height) to 20
13→# - Replace clear.screen/move.cursor to avoid book library dependencies
14→# - Add channel direction annotations (? and !) for occam2go compatibility
15→# - Wrap main body in PROC life(...) declaration
16→#
17→# All line-number references are to the original downloaded file, which has not
18→# changed since 1988. Edits are applied bottom-to-top so that line numbers of
19→# earlier edits remain valid.
20→
21→set -euo pipefail
22→
23→URL="https://www.cs.ox.ac.uk/geraint.jones/publications/book/Pio2/code-life.txt"
24→SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
25→OUTPUT="${SCRIPT_DIR}/life.occ"
26→
27→echo "Downloading original life.occ from book website..."
28→if ! curl -sfS -o "${OUTPUT}" "${URL}"; then
29→ echo "Error: failed to download ${URL}" >&2
30→ exit 1
31→fi
32→
33→echo "Applying modifications for occam2go..."
34→
35→# We apply edits bottom-to-top so earlier line numbers stay correct.
36→
37→# --- Main body (lines 396-411): wrap in PROC life, indent, rename params ---
38→# Insert PROC header before line 396
39→sed -i '396i\PROC life (CHAN BYTE keyboard?, screen!, error!)' "${OUTPUT}"
40→# Indent lines 397-412 (the original 396-411, now shifted by 1) by 2 spaces
41→sed -i '397,412s/^/ /' "${OUTPUT}"
42→# Rename terminal.keyboard -> keyboard, terminal.screen -> screen in that range
43→sed -i '397,412s/terminal\.keyboard/keyboard/' "${OUTPUT}"
44→sed -i '397,412s/terminal\.screen/screen/' "${OUTPUT}"
45→# Append closing colon after line 412
46→sed -i '412a\:' "${OUTPUT}"
47→# Append trailing blank line
48→sed -i '413a\\' "${OUTPUT}"
49→
50→# --- controller (line 345): add channel direction annotations ---
51→sed -i '345s/keyboard, screen,/keyboard?, screen!,/' "${OUTPUT}"
52→
53→# --- editor (line 290): add channel direction annotations ---
54→sed -i '290s/keyboard, screen,/keyboard?, screen!,/' "${OUTPUT}"
55→
56→# --- generation (line 208): add ! to screen param ---
57→sed -i '208s/screen,/screen!,/' "${OUTPUT}"
58→
59→# --- display.activity (line 188): add ! to screen param ---
60→sed -i '188s/screen,/screen!,/' "${OUTPUT}"
61→
62→# --- display.state (line 150): add ! to screen param ---
63→sed -i '150s/screen,/screen!,/' "${OUTPUT}"
64→
65→# --- clean.up.display (line 146): add ! to screen param ---
66→sed -i '146s/screen)/screen!)/' "${OUTPUT}"
67→
68→# --- initialize.display (line 141): add ! to screen param ---
69→sed -i '141s/screen)/screen!)/' "${OUTPUT}"
70→
71→# --- move.cursor (lines 124-131): replace signature and body ---
72→# Line 124: add ! to terminal param
73→sed -i '124s/terminal,/terminal!,/' "${OUTPUT}"
74→# Lines 126-131: delete old body (DATA.ITEM/write.formatted), insert new
75→sed -i '126,131d' "${OUTPUT}"
76→# Insert new body at line 126 (after the comment on line 125)
77→sed -i '125a\
78→ -- outputs ANSI escape sequence: ESC [ row ; col H\
79→ SEQ\
80→ terminal ! BYTE #1B\
81→ terminal ! '"'"'['"'"'\
82→ write.small.int(terminal, y + 1)\
83→ terminal ! '"'"';'"'"'\
84→ write.small.int(terminal, x + 1)\
85→ terminal ! '"'"'H'"'"'' "${OUTPUT}"
86→
87→# --- clear.screen (lines 119-121): replace implementation ---
88→# Line 119: add ! to terminal param
89→sed -i '119s/terminal)/terminal!)/' "${OUTPUT}"
90→# Line 120: replace comment (add explicit chars description)
91→sed -i '120s/-- clear screen sequence for an ANSI terminal/-- clear screen sequence for an ANSI terminal: ESC [ 2 J/' "${OUTPUT}"
92→# Line 121: delete old one-liner body
93→sed -i '121d' "${OUTPUT}"
94→# Insert new multi-line body after line 120
95→sed -i '120a\
96→ SEQ\
97→ terminal ! BYTE #1B\
98→ terminal ! '"'"'['"'"'\
99→ terminal ! '"'"'2'"'"'\
100→ terminal ! '"'"'J'"'"'' "${OUTPUT}"
101→
102→# --- array dimensions (lines 8-9): replace ... with 20 ---
103→sed -i '8s/IS \.\.\. :/IS 20 :/' "${OUTPUT}"
104→sed -i '9s/IS \.\.\. :/IS 20 :/' "${OUTPUT}"
105→
106→# --- Insert adaptation comment and helper procs after line 2 ---
107→sed -i '2a\
108→-- Adapted for occam2go: replaced book-library functions\
109→-- (write.string, write.formatted, DATA.ITEM) with inline\
110→-- definitions; added terminal.keyboard/terminal.screen declarations.\
111→--\
112→\
113→--\
114→-- helper procedures (replaces book standard library)\
115→--\
116→\
117→PROC write.string(CHAN OF BYTE out!, VAL []BYTE s)\
118→ SEQ i = 0 FOR SIZE s\
119→ out ! s[i]\
120→:\
121→\
122→PROC write.small.int(CHAN OF BYTE out!, VAL INT n)\
123→ -- outputs a small non-negative integer (0..999) as decimal digits\
124→ IF\
125→ n >= 100\
126→ SEQ\
127→ out ! BYTE ((n / 100) + (INT '"'"'0'"'"'))\
128→ out ! BYTE (((n / 10) \\ 10) + (INT '"'"'0'"'"'))\
129→ out ! BYTE ((n \\ 10) + (INT '"'"'0'"'"'))\
130→ n >= 10\
131→ SEQ\
132→ out ! BYTE ((n / 10) + (INT '"'"'0'"'"'))\
133→ out ! BYTE ((n \\ 10) + (INT '"'"'0'"'"'))\
134→ TRUE\
135→ out ! BYTE (n + (INT '"'"'0'"'"'))\
136→:' "${OUTPUT}"
137→
138→# --- Insert copyright/attribution header at the very top ---
139→sed -i '1i\
140→-- Code copied from Programming in occam®2\
141→-- © Geraint Jones, Michael Goldsmith 1988, 2001.\
142→-- Permission is granted to copy this material for private study; for other uses please contact occam-book@comlab.ox.ac.uk\
143→--' "${OUTPUT}"
144→
145→echo "Done. Output written to: ${OUTPUT}"
146→{
"file_path": "/home/david/projects/codeassociates/occam2go/historical-examples/life.occ"
} 1→-- Code copied from Programming in occam®2
2→-- © Geraint Jones, Michael Goldsmith 1988, 2001.
3→-- Permission is granted to copy this material for private study; for other uses please contact occam-book@comlab.ox.ac.uk
4→--
5→-- The program in this chapter plays Life on a terminal screen.
6→--
7→-- Adapted for occam2go: replaced book-library functions
8→-- (write.string, write.formatted, DATA.ITEM) with inline
9→-- definitions; added terminal.keyboard/terminal.screen declarations.
10→--
11→
12→--
13→-- helper procedures (replaces book standard library)
14→--
15→
16→PROC write.string(CHAN OF BYTE out!, VAL []BYTE s)
17→ SEQ i = 0 FOR SIZE s
18→ out ! s[i]
19→:
20→
21→PROC write.small.int(CHAN OF BYTE out!, VAL INT n)
22→ -- outputs a small non-negative integer (0..999) as decimal digits
23→ IF
24→ n >= 100
25→ SEQ
26→ out ! BYTE ((n / 100) + (INT '0'))
27→ out ! BYTE (((n / 10) \ 10) + (INT '0'))
28→ out ! BYTE ((n \ 10) + (INT '0'))
29→ n >= 10
30→ SEQ
31→ out ! BYTE ((n / 10) + (INT '0'))
32→ out ! BYTE ((n \ 10) + (INT '0'))
33→ TRUE
34→ out ! BYTE (n + (INT '0'))
35→:
36→
37→--
38→-- configuration constants
39→--
40→
41→VAL INT array.width IS 20 : -- number of cells across the board
42→VAL INT array.height IS 20 : -- number of cells down the board
43→
44→VAL INT radius IS 1 : -- of the `sphere of influence'
45→VAL INT diameter IS (2 * radius) + 1 :
46→VAL INT neighbours IS (diameter * diameter) - 1 :
47→
48→VAL INT number.of.cells IS array.height * array.width :
49→VAL INT number.of.links IS neighbours * number.of.cells :
50→
51→--
52→-- protocols
53→--
54→
55→PROTOCOL STATE IS BOOL :
56→
57→VAL BOOL alive IS TRUE :
58→VAL BOOL dead IS NOT alive :
59→
60→PROTOCOL COMMAND
61→ CASE
62→ set.state; BOOL
63→ evolve
64→ terminate
65→:
66→
67→PROTOCOL RESPONSE IS BOOL; BOOL :
68→--
69→-- cell processes
70→--
71→
72→PROC broadcast.present.state([][][]CHAN OF STATE link,
73→ VAL INT x, y, VAL BOOL state )
74→ PAR d = 0 FOR neighbours
75→ link[x][y][d] ! state
76→:
77→
78→PROC calculate.next.state([][][]CHAN OF STATE link,
79→ VAL []INT nx, ny,
80→ VAL BOOL state, BOOL next.state )
81→ INT count : -- number of living neighbours
82→ SEQ
83→ [neighbours]BOOL state.of.neighbour :
84→ SEQ
85→ PAR d = 0 FOR neighbours
86→ link[nx[d]][ny[d]][d] ? state.of.neighbour[d]
87→ count := 0
88→ SEQ d = 0 FOR neighbours
89→ IF
90→ state.of.neighbour[d] = alive
91→ count := count + 1
92→ state.of.neighbour[d] = dead
93→ SKIP
94→ IF
95→ count < 2 -- death from isolation
96→ next.state := dead
97→ count = 2 -- this cell is stable
98→ next.state := state
99→ count = 3 -- stable if alive, a birth if dead
100→ next.state := alive
101→ count > 3 -- death from overcrowding
102→ next.state := dead
103→:
104→
105→
106→
107→
108→
109→
110→
111→
112→
113→
114→
115→
116→
117→
118→
119→PROC cell([][][]CHAN OF STATE link,
120→ VAL INT x, y, VAL []INT nx, ny,
121→ CHAN OF COMMAND control,
122→ CHAN OF RESPONSE sense )
123→ BOOL state, not.finished :
124→ SEQ
125→ state := dead -- the whole board starts off dead
126→ not.finished := TRUE
127→ WHILE not.finished
128→ control ? CASE
129→
130→ set.state; state
131→ SKIP -- state has been set to the new value
132→
133→ evolve
134→ BOOL next.state :
135→ SEQ
136→ PAR
137→ broadcast.present.state(link, x, y, state)
138→ SEQ
139→ calculate.next.state(link, nx, ny,
140→ state, next.state )
141→ sense ! (state <> next.state); next.state
142→ state := next.state
143→
144→ terminate
145→ not.finished := FALSE
146→:
147→
148→--
149→-- terminal-dependent output routines
150→--
151→
152→PROC clear.screen(CHAN OF BYTE terminal!)
153→ -- clear screen sequence for an ANSI terminal: ESC [ 2 J
154→ SEQ
155→ terminal ! BYTE #1B
156→ terminal ! '['
157→ terminal ! '2'
158→ terminal ! 'J'
159→:
160→
161→PROC move.cursor(CHAN OF BYTE terminal!, VAL INT x, y)
162→ -- left-handed co-ordinates, origin 0,0 at top left
163→ -- outputs ANSI escape sequence: ESC [ row ; col H
164→ SEQ
165→ terminal ! BYTE #1B
166→ terminal ! '['
167→ write.small.int(terminal, y + 1)
168→ terminal ! ';'
169→ write.small.int(terminal, x + 1)
170→ terminal ! 'H'
171→:
172→
173→
174→
175→
176→--
177→-- display routines
178→--
179→
180→PROC initialize.display(CHAN OF BYTE screen!)
181→ -- display an entirely dead board
182→ clear.screen(screen)
183→:
184→
185→PROC clean.up.display(CHAN OF BYTE screen!)
186→ move.cursor(screen, 0, array.height)
187→:
188→
189→PROC display.state(CHAN OF BYTE screen!, VAL INT x, y, VAL BOOL state)
190→ SEQ
191→ move.cursor(screen, x, y)
192→ IF
193→ state = alive
194→ screen ! '**'
195→ state = dead
196→ screen ! '*s'
197→:
198→
199→--
200→-- controller states
201→--
202→
203→VAL INT idle IS 0 : -- controller activity values
204→VAL INT editing IS 1 :
205→VAL INT single.step IS 2 :
206→VAL INT free.running IS 3 :
207→VAL INT terminated IS 4 :
208→
209→
210→INT FUNCTION new.activity(VAL BYTE char)
211→ INT activity :
212→ VALOF
213→ CASE char -- typed on the keyboard ...
214→ 'q', 'Q' -- ... Q to finish program
215→ activity := terminated
216→ 's', 'S' -- ... S to halt evolution
217→ activity := idle
218→ 'e', 'E' -- ... E to start editing
219→ activity := editing
220→ 'r', 'R' -- ... R to start evolution
221→ activity := free.running
222→ ELSE -- ... or anything else for one generation
223→ activity := single.step
224→ RESULT activity
225→:
226→
227→PROC display.activity(CHAN OF BYTE screen!, VAL INT activity)
228→ SEQ
229→ move.cursor(screen, array.width+1, array.height/2)
230→ CASE activity
231→ idle
232→ write.string(screen, "Idle")
233→ editing
234→ write.string(screen, "Edit")
235→ single.step
236→ write.string(screen, "Step")
237→ free.running
238→ write.string(screen, "Busy")
239→ terminated
240→ write.string(screen, "Done")
241→:
242→
243→--
244→-- generation
245→--
246→
247→PROC generation(CHAN OF BYTE screen!,
248→ [][]CHAN OF COMMAND control,
249→ [][]CHAN OF RESPONSE sense,
250→ BOOL active )
251→ SEQ
252→ PAR x = 0 FOR array.width
253→ PAR y = 0 FOR array.height
254→ control[x][y] ! evolve
255→ active := FALSE
256→ SEQ x = 0 FOR array.width
257→ SEQ y = 0 FOR array.height
258→ BOOL changed, next.state :
259→ SEQ
260→ sense[x][y] ? changed; next.state
261→ IF
262→ changed
263→ SEQ
264→ display.state(screen, x, y, next.state)
265→ active := TRUE
266→ NOT changed
267→ SKIP
268→:
269→
270→
271→
272→
273→
274→
275→
276→
277→
278→--
279→-- editor
280→--
281→
282→INT FUNCTION min(VAL INT a, b)
283→ INT min :
284→ VALOF
285→ IF
286→ a <= b
287→ min := a
288→ b <= a
289→ min := b
290→ RESULT min
291→:
292→
293→INT FUNCTION max(VAL INT a, b)
294→ INT max :
295→ VALOF
296→ IF
297→ a >= b
298→ max := a
299→ b >= a
300→ max := b
301→ RESULT max
302→:
303→
304→
305→
306→
307→
308→
309→
310→
311→
312→
313→
314→
315→
316→
317→
318→
319→
320→
321→
322→
323→
324→
325→
326→
327→
328→
329→PROC editor(CHAN OF BYTE keyboard?, screen!,
330→ [][]CHAN OF COMMAND control )
331→ INT x, y :
332→ BOOL editing :
333→ SEQ
334→ -- initialize co-ordinates to centre of board
335→ x, y := array.width / 2, array.height / 2
336→ editing := TRUE
337→ WHILE editing
338→ BYTE char :
339→ SEQ
340→ move.cursor(screen, x, y)
341→ keyboard ? char
342→ CASE char
343→ 'A' -- move up, if possible
344→ y := max(y - 1, 0)
345→ 'B' -- move down, if possible
346→ y := min(y + 1, array.height - 1)
347→ 'C' -- move right, if possible
348→ x := min(x + 1, array.width - 1)
349→ 'D' -- move left, if possible
350→ x := max(x - 1, 0)
351→ '*s', '**'
352→ VAL BOOL state IS (char = '**') = alive :
353→ PAR
354→ control[x][y] ! set.state; state
355→ display.state(screen, x, y, state)
356→ 'q', 'Q'
357→ editing := FALSE
358→ ELSE
359→ SKIP -- ignore anything else
360→:
361→
362→
363→
364→
365→
366→
367→
368→
369→
370→
371→
372→
373→
374→
375→
376→
377→
378→
379→
380→--
381→-- controller
382→--
383→
384→PROC controller(CHAN OF BYTE keyboard?, screen!,
385→ [][]CHAN OF COMMAND control,
386→ [][]CHAN OF RESPONSE sense )
387→ INT activity :
388→ SEQ
389→ activity := idle
390→ initialize.display(screen)
391→ WHILE activity <> terminated
392→ SEQ
393→ display.activity(screen, activity)
394→ BYTE char :
395→ PRI ALT
396→ (activity <> editing) & keyboard ? char
397→ activity := new.activity(char)
398→ (activity <> idle) & SKIP
399→ CASE activity
400→ editing
401→ SEQ
402→ editor(keyboard, screen, control)
403→ activity := idle
404→ free.running, single.step
405→ BOOL changing :
406→ SEQ
407→ generation(screen, control, sense, changing)
408→ IF
409→ (activity = single.step) OR (NOT changing)
410→ activity := idle
411→ (activity = free.running) AND changing
412→ SKIP
413→ display.activity(screen, activity)
414→ PAR x = 0 FOR array.width
415→ PAR y = 0 FOR array.height
416→ control[x][y] ! terminate
417→ clean.up.display(screen)
418→:
419→
420→
421→
422→
423→
424→
425→
426→
427→
428→
429→
430→
431→--
432→-- structure of the program
433→--
434→
435→PROC life (CHAN BYTE keyboard?, screen!, error!)
436→ [array.width][array.height][neighbours]CHAN OF STATE link :
437→ [array.width][array.height]CHAN OF COMMAND control :
438→ [array.width][array.height]CHAN OF RESPONSE sense :
439→ PAR
440→ controller(keyboard, screen, control, sense)
441→ PAR x = 0 FOR array.width
442→ PAR y = 0 FOR array.height
443→ VAL INT left IS ((x - 1) + array.width) \ array.width :
444→ VAL INT right IS (x + 1) \ array.width :
445→ VAL INT up IS (y + 1) \ array.height :
446→ VAL INT down IS ((y - 1) + array.height) \ array.height :
447→ VAL [neighbours]INT nx IS
448→ [ right, x, left, left, left, x, right, right ] :
449→ VAL [neighbours]INT ny IS
450→ [ down, down, down, y, up, up, up, y ] :
451→ cell(link, x, y, nx, ny, control[x][y], sense[x][y])
452→:
453→
454→{
"pattern": "life\\.occ",
"path": "/home/david/projects/codeassociates/occam2go/.gitignore",
"output_mode": "content"
}So the task is:
1. The life.occ file already exists in historical-examples/ (generated by the fetch script)
2. We need to commit it directly to the repo instead
3. Remove the fetch-life.sh script
4. Remove the .gitignore entry for historical-examples/life.occ
5. Update the README to reflect that the file is now in the repo
Let me also check the README.
Now I understand the full picture. The task is to:
historical-examples/life.occ file directly to the repofetch-life.sh download script (no longer needed).gitignore entry for historical-examples/life.occhistorical-examples/README.md to reflect the file is now in the repoLet me read the remaining files I need to update.
{
"file_path": "/home/david/projects/codeassociates/occam2go/historical-examples/README.md"
} 1→# Historical Examples
2→
3→This directory contains example programs from historical occam publications, adapted to build and run with occam2go.
4→
5→## Life (Conway's Game of Life)
6→
7→From *Programming in occam 2* by Geraint Jones and Michael Goldsmith (1988).
8→Book website: https://www.cs.ox.ac.uk/geraint.jones/publications/book/Pio2/
9→
10→The original source is copyrighted and not included in this repository. To obtain it, run the fetch script which downloads it from the book website and applies the modifications needed for occam2go:
11→
12→```bash
13→./historical-examples/fetch-life.sh
14→```
15→
16→This produces `historical-examples/life.occ`, which can then be transpiled and run:
17→
18→```bash
19→./occam2go -o life.go historical-examples/life.occ
20→go run life.go
21→```
22→
23→### Controls
24→
25→- **E** — enter editor mode (use arrow keys to move, space/asterisk to toggle cells, Q to exit editor)
26→- **R** — run (free-running evolution)
27→- **S** — stop
28→- **Any other key** — single step
29→- **Q** — quit
30→ 1→# Binaries
2→occam2go
3→*.exe
4→*.exe~
5→*.dll
6→*.so
7→*.dylib
8→
9→# Test binaries
10→*.test
11→
12→# Output of go coverage
13→*.out
14→
15→# Go workspace file
16→go.work
17→
18→# Dependency directories
19→vendor/
20→
21→# IDE
22→.idea/
23→.vscode/
24→*.swp
25→*.swo
26→
27→# OS
28→.DS_Store
29→Thumbs.db
30→
31→# Generated Go files from examples
32→examples/*.go
33→
34→# Generated occam files from fetch scripts
35→historical-examples/life.occ
36→
37→# External repositories
38→kroc/
39→# Generated occam files from fetch scripts historical-examples/life.occ
The original source is copyrighted and not included in this repository. To obtain it, run the fetch script which downloads it from the book website and applies the modifications needed for occam2go: ```bash ./historical-examples/fetch-life.sh ``` This produces `historical-examples/life.occ`, which can then be transpiled and run:
The source is included in this repository with permission from the copyright holders, with modifications for occam2go compatibility. It can be transpiled and run:
rm /home/david/projects/codeassociates/occam2go/historical-examples/fetch-life.sh
The file /home/david/projects/codeassociates/occam2go/.gitignore has been updated successfully.
The file /home/david/projects/codeassociates/occam2go/historical-examples/README.md has been updated successfully.
All changes are made. Here's a summary:
historical-examples/fetch-life.sh — the download/patch script is no longer needed.gitignore entry for historical-examples/life.occ — so the file can be committedhistorical-examples/README.md — replaced the "download and run the script" instructions with a note that the source is included with permissionThe existing historical-examples/life.occ (already generated by the fetch script) will now be tracked by git. Want me to commit these changes and/or create a PR?
On branch main Your branch is up to date with 'origin/main'. Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: .gitignore modified: historical-examples/README.md deleted: historical-examples/fetch-life.sh Untracked files: (use "git add <file>..." to include in what will be committed) historical-examples/life.occ life.go no changes added to commit (use "git add" and/or "git commit -a")
diff --git a/.gitignore b/.gitignore
index 65c1dcc..3f86ba6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,8 +31,6 @@ Thumbs.db
# Generated Go files from examples
examples/*.go
-# Generated occam files from fetch scripts
-historical-examples/life.occ
# External repositories
kroc/
diff --git a/historical-examples/README.md b/historical-examples/README.md
index c42bae4..8661913 100644
--- a/historical-examples/README.md
+++ b/historical-examples/README.md
@@ -7,13 +7,7 @@ This directory contains example programs from historical occam publications, ada
From *Programming in occam 2* by Geraint Jones and Michael Goldsmith (1988).
Book website: https://www.cs.ox.ac.uk/geraint.jones/publications/book/Pio2/
-The original source is copyrighted and not included in this repository. To obtain it, run the fetch script which downloads it from the book website and applies the modifications needed for occam2go:
-
-```bash
-./historical-examples/fetch-life.sh
-```
-
-This produces `historical-examples/life.occ`, which can then be transpiled and run:
+The source is included in this repository with permission from the copyright holders, with modifications for occam2go compatibility. It can be transpiled and run:
```bash
./occam2go -o life.go historical-examples/life.occ
diff --git a/historical-examples/fetch-life.sh b/historical-examples/fetch-life.sh
deleted file mode 100755
index bff0327..0000000
--- a/historical-examples/fetch-life.sh
+++ /dev/null
@@ -1,145 +0,0 @@
-#!/bin/bash
-#
-# Downloads the Life example from the "Programming in occam 2" book website
-# and applies modifications needed to build with occam2go and the kroc course module.
-#
-# The original source is copyright Geraint Jones & Michael Goldsmith 1988, 2001.
-# See: https://www.cs.ox.ac.uk/geraint.jones/publications/book/Pio2/code-life.txt
-#
-# Changes applied:
-# - Add copyright/attribution header and adaptation notes
-# - Add helper procedures (write.string, write.small.int) replacing book library
-# - Set board dimensions (array.width/array.height) to 20
-# - Replace clear.screen/move.cursor to avoid book library dependencies
-# - Add channel direction annotations (? and !) for occam2go compatibility
-# - Wrap main body in PROC life(...) declaration
-#
-# All line-number references are to the original downloaded file, which has not
-# changed since 1988. Edits are applied bottom-to-top so that line numbers of
-# earlier edits remain valid.
-
-set -euo pipefail
-
-URL="https://www.cs.ox.ac.uk/geraint.jones/publications/book/Pio2/code-life.txt"
-SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
-OUTPUT="${SCRIPT_DIR}/life.occ"
-
-echo "Downloading original life.occ from book website..."
-if ! curl -sfS -o "${OUTPUT}" "${URL}"; then
- echo "Error: failed to download ${URL}" >&2
- exit 1
-fi
-
-echo "Applying modifications for occam2go..."
-
-# We apply edits bottom-to-top so earlier line numbers stay correct.
-
-# --- Main body (lines 396-411): wrap in PROC life, indent, rename params ---
-# Insert PROC header before line 396
-sed -i '396i\PROC life (CHAN BYTE keyboard?, screen!, error!)' "${OUTPUT}"
-# Indent lines 397-412 (the original 396-411, now shifted by 1) by 2 spaces
-sed -i '397,412s/^/ /' "${OUTPUT}"
-# Rename terminal.keyboard -> keyboard, terminal.screen -> screen in that range
-sed -i '397,412s/terminal\.keyboard/keyboard/' "${OUTPUT}"
-sed -i '397,412s/terminal\.screen/screen/' "${OUTPUT}"
-# Append closing colon after line 412
-sed -i '412a\:' "${OUTPUT}"
-# Append trailing blank line
-sed -i '413a\\' "${OUTPUT}"
-
-# --- controller (line 345): add channel direction annotations ---
-sed -i '345s/keyboard, screen,/keyboard?, screen!,/' "${OUTPUT}"
-
-# --- editor (line 290): add channel direction annotations ---
-sed -i '290s/keyboard, screen,/keyboard?, screen!,/' "${OUTPUT}"
-
-# --- generation (line 208): add ! to screen param ---
-sed -i '208s/screen,/screen!,/' "${OUTPUT}"
-
-# --- display.activity (line 188): add ! to screen param ---
-sed -i '188s/screen,/screen!,/' "${OUTPUT}"
-
-# --- display.state (line 150): add ! to screen param ---
-sed -i '150s/screen,/screen!,/' "${OUTPUT}"
-
-# --- clean.up.display (line 146): add ! to screen param ---
-sed -i '146s/screen)/screen!)/' "${OUTPUT}"
-
-# --- initialize.display (line 141): add ! to screen param ---
-sed -i '141s/screen)/screen!)/' "${OUTPUT}"
-
-# --- move.cursor (lines 124-131): replace signature and body ---
-# Line 124: add ! to terminal param
-sed -i '124s/terminal,/terminal!,/' "${OUTPUT}"
-# Lines 126-131: delete old body (DATA.ITEM/write.formatted), insert new
-sed -i '126,131d' "${OUTPUT}"
-# Insert new body at line 126 (after the comment on line 125)
-sed -i '125a\
- -- outputs ANSI escape sequence: ESC [ row ; col H\
- SEQ\
- terminal ! BYTE #1B\
- terminal ! '"'"'['"'"'\
- write.small.int(terminal, y + 1)\
- terminal ! '"'"';'"'"'\
- write.small.int(terminal, x + 1)\
- terminal ! '"'"'H'"'"'' "${OUTPUT}"
-
-# --- clear.screen (lines 119-121): replace implementation ---
-# Line 119: add ! to terminal param
-sed -i '119s/terminal)/terminal!)/' "${OUTPUT}"
-# Line 120: replace comment (add explicit chars description)
-sed -i '120s/-- clear screen sequence for an ANSI terminal/-- clear screen sequence for an ANSI terminal: ESC [ 2 J/' "${OUTPUT}"
-# Line 121: delete old one-liner body
-sed -i '121d' "${OUTPUT}"
-# Insert new multi-line body after line 120
-sed -i '120a\
- SEQ\
- terminal ! BYTE #1B\
- terminal ! '"'"'['"'"'\
- terminal ! '"'"'2'"'"'\
- terminal ! '"'"'J'"'"'' "${OUTPUT}"
-
-# --- array dimensions (lines 8-9): replace ... with 20 ---
-sed -i '8s/IS \.\.\. :/IS 20 :/' "${OUTPUT}"
-sed -i '9s/IS \.\.\. :/IS 20 :/' "${OUTPUT}"
-
-# --- Insert adaptation comment and helper procs after line 2 ---
-sed -i '2a\
--- Adapted for occam2go: replaced book-library functions\
--- (write.string, write.formatted, DATA.ITEM) with inline\
--- definitions; added terminal.keyboard/terminal.screen declarations.\
---\
-\
---\
--- helper procedures (replaces book standard library)\
---\
-\
-PROC write.string(CHAN OF BYTE out!, VAL []BYTE s)\
- SEQ i = 0 FOR SIZE s\
- out ! s[i]\
-:\
-\
-PROC write.small.int(CHAN OF BYTE out!, VAL INT n)\
- -- outputs a small non-negative integer (0..999) as decimal digits\
- IF\
- n >= 100\
- SEQ\
- out ! BYTE ((n / 100) + (INT '"'"'0'"'"'))\
- out ! BYTE (((n / 10) \\ 10) + (INT '"'"'0'"'"'))\
- out ! BYTE ((n \\ 10) + (INT '"'"'0'"'"'))\
- n >= 10\
- SEQ\
- out ! BYTE ((n / 10) + (INT '"'"'0'"'"'))\
- out ! BYTE ((n \\ 10) + (INT '"'"'0'"'"'))\
- TRUE\
- out ! BYTE (n + (INT '"'"'0'"'"'))\
-:' "${OUTPUT}"
-
-# --- Insert copyright/attribution header at the very top ---
-sed -i '1i\
--- Code copied from Programming in occam®2\
--- © Geraint Jones, Michael Goldsmith 1988, 2001.\
--- Permission is granted to copy this material for private study; for other uses please contact occam-book@comlab.ox.ac.uk\
---' "${OUTPUT}"
-
-echo "Done. Output written to: ${OUTPUT}"c856a5e Merge pull request #96 from codeassociates/dboreham/update-readme 1b2ff03 Update README with project background and article link baa42a2 Merge pull request #95 from codeassociates/fix/replace-life-with-fetch-script-94 7bb1257 Add README for historical-examples directory cf76024 Replace copyrighted life.occ with download-and-patch script (#94)
git add .gitignore historical-examples/README.md historical-examples/fetch-life.sh historical-examples/life.occ
git commit -m "$(cat <<'EOF' Include life.occ directly in repository Now that we have permission from the copyright holders, commit the modified life.occ source directly instead of downloading it at build time. Remove the fetch-life.sh script and its .gitignore entry, and update the README accordingly. Closes #97 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> EOF )"
4 files changed, 454 insertions(+), 154 deletions(-) delete mode 100755 historical-examples/fetch-life.sh create mode 100644 historical-examples/life.occ
remote: remote: Create a pull request for 'include-life-occ-97' on GitHub by visiting: remote: https://github.com/codeassociates/occam2go/pull/new/include-life-occ-97 remote: To associate-3.github.com:codeassociates/occam2go.git * [new branch] include-life-occ-97 -> include-life-occ-97 branch 'include-life-occ-97' set up to track 'origin/include-life-occ-97'.
gh pr create --title "Include life.occ directly in repository" --body "$(cat <<'EOF' ## Summary - Commit the modified `life.occ` source directly now that we have copyright holder permission - Remove the `fetch-life.sh` download/patch script (no longer needed) - Remove the `.gitignore` entry for `historical-examples/life.occ` - Update `historical-examples/README.md` to reflect the file is included directly Closes #97 ## Test plan - [ ] Verify `historical-examples/life.occ` is present and matches the previously generated output - [ ] Verify `./occam2go -o life.go historical-examples/life.occ && go run life.go` still works - [ ] Verify `fetch-life.sh` is removed 🤖 Generated with [Claude Code](https://claude.com/claude-code) EOF )"
PR created: https://github.com/codeassociates/occam2go/pull/98
The warning about 1 uncommitted change is just the untracked life.go file in the repo root (the transpiled output), which isn't part of this change.