Please take a look at issue 1 for this project, generate a fix on a suitable new branch then create a PR for the fix.
title: occam2go command line options are incorrectly documented state: OPEN author: dboreham labels: comments: 0 assignees: projects: milestone: number: 1 -- In the readme, it says (consistently, a few times) that this is the syntax for running occam2go: ``` ./occam2go <input-file> -o <output-file> ``` Whereas the code in fact implements this: ``` ./occam2go -o <output-file> <input-file> ``` The documentation should match the program behavior.
1→# occam2go
2→
3→A transpiler from Occam to Go, written in Go.
4→
5→Occam was developed in the 1980s to support concurrent programming on the Transputer. Go, created decades later, shares similar CSP-influenced concurrency primitives. This transpiler bridges the two.
6→
7→## Building
8→
9→```bash
10→go build -o occam2go
11→```
12→
13→## Usage
14→
15→```bash
16→./occam2go [options] <input.occ>
17→```
18→
19→Options:
20→- `-o <file>` - Write output to file (default: stdout)
21→- `-version` - Print version and exit
22→
23→## Running an Example
24→
25→Here's how to transpile, compile, and run an Occam program:
26→
27→```bash
28→# 1. Build the transpiler (only needed once)
29→go build -o occam2go
30→
31→# 2. Transpile an Occam file to Go
32→./occam2go examples/print.occ -o output.go
33→
34→# 3. Compile the generated Go code
35→go build -o output output.go
36→
37→# 4. Run the compiled program
38→./output
39→```
40→
41→Or as a one-liner to see the output immediately:
42→
43→```bash
44→./occam2go examples/print.occ -o output.go && go run output.go
45→```
46→
47→## Example
48→
49→Input (`example.occ`):
50→```occam
51→SEQ
52→ INT x, y:
53→ PAR
54→ x := 1
55→ y := 2
56→ x := x + y
57→```
58→
59→Output:
60→```go
61→package main
62→
63→import (
64→ "sync"
65→)
66→
67→func main() {
68→ var x, y int
69→ var wg sync.WaitGroup
70→ wg.Add(2)
71→ go func() {
72→ defer wg.Done()
73→ x = 1
74→ }()
75→ go func() {
76→ defer wg.Done()
77→ y = 2
78→ }()
79→ wg.Wait()
80→ x = (x + y)
81→}
82→```
83→
84→## Implemented Features
85→
86→See [TODO.md](TODO.md) for the full implementation status and roadmap.
87→
88→| Occam | Go |
89→|-------|-----|
90→| `INT`, `BYTE`, `BOOL`, `REAL` | `int`, `byte`, `bool`, `float64` |
91→| `SEQ` | Sequential code |
92→| `PAR` | Goroutines with `sync.WaitGroup` |
93→| `IF` | `if / else if` |
94→| `WHILE` | `for` loop |
95→| `STOP` | Print to stderr + `select {}` (deadlock) |
96→| `PROC` with `VAL` params | Functions with value/pointer params |
97→| `:=` assignment | `=` assignment |
98→| Arithmetic: `+`, `-`, `*`, `/`, `\` | `+`, `-`, `*`, `/`, `%` |
99→| Comparison: `=`, `<>`, `<`, `>`, `<=`, `>=` | `==`, `!=`, `<`, `>`, `<=`, `>=` |
100→| Logic: `AND`, `OR`, `NOT` | `&&`, `\|\|`, `!` |
101→| Bitwise: `/\`, `\/`, `><`, `~` | `&`, `\|`, `^`, `^` (AND, OR, XOR, NOT) |
102→| Shifts: `<<`, `>>` | `<<`, `>>` |
103→| Type conversions: `INT x`, `BYTE n` | `int(x)`, `byte(n)` |
104→
105→### Channels
106→
107→| Occam | Go |
108→|-------|-----|
109→| `CHAN OF INT c:` | `c := make(chan int)` |
110→| `c ! x` (send) | `c <- x` |
111→| `c ? y` (receive) | `y = <-c` |
112→| `[5]CHAN OF INT cs:` | `cs := make([]chan int, 5)` + init loop |
113→| `cs[i] ! x` (indexed send) | `cs[i] <- x` |
114→| `cs[i] ? y` (indexed receive) | `y = <-cs[i]` |
115→| `PROC f([]CHAN OF INT cs)` | `func f(cs []chan int)` |
116→
117→Example:
118→```occam
119→SEQ
120→ CHAN OF INT c:
121→ INT result:
122→ PAR
123→ c ! 42
124→ c ? result
125→ print.int(result)
126→```
127→
128→Channel array example:
129→```occam
130→SEQ
131→ [3]CHAN OF INT cs:
132→ INT sum:
133→ sum := 0
134→ PAR
135→ PAR i = 0 FOR 3
136→ cs[i] ! (i + 1) * 10
137→ SEQ i = 0 FOR 3
138→ INT x:
139→ cs[i] ? x
140→ sum := sum + x
141→ print.int(sum)
142→```
143→
144→### Protocols
145→
146→Protocols define the type of data carried on a channel. Three forms are supported:
147→
148→| Occam | Go |
149→|-------|-----|
150→| `PROTOCOL SIG IS INT` | `type _proto_SIG = int` |
151→| `PROTOCOL PAIR IS INT ; BYTE` | `type _proto_PAIR struct { _0 int; _1 byte }` |
152→| `PROTOCOL MSG CASE tag; INT ...` | Interface + concrete structs per tag |
153→| `c ! 42 ; 65` (sequential send) | `c <- _proto_PAIR{42, 65}` |
154→| `c ? x ; y` (sequential recv) | `_tmp := <-c; x = _tmp._0; y = _tmp._1` |
155→| `c ! tag ; val` (variant send) | `c <- _proto_MSG_tag{val}` |
156→| `c ? CASE ...` (variant recv) | `switch _v := (<-c).(type) { ... }` |
157→
158→Sequential protocol example:
159→```occam
160→PROTOCOL PAIR IS INT ; INT
161→
162→SEQ
163→ CHAN OF PAIR c:
164→ INT x, y:
165→ PAR
166→ c ! 10 ; 20
167→ c ? x ; y
168→ print.int(x + y)
169→```
170→
171→Variant protocol example:
172→```occam
173→PROTOCOL MSG
174→ CASE
175→ data; INT
176→ quit
177→
178→SEQ
179→ CHAN OF MSG c:
180→ INT result:
181→ PAR
182→ c ! data ; 42
183→ c ? CASE
184→ data ; result
185→ print.int(result)
186→ quit
187→ SKIP
188→```
189→
190→### Records
191→
192→| Occam | Go |
193→|-------|-----|
194→| `RECORD POINT` with `INT x:` `INT y:` | `type POINT struct { x int; y int }` |
195→| `POINT p:` | `var p POINT` |
196→| `p[x] := 10` | `p.x = 10` |
197→| `p[x]` (in expression) | `p.x` |
198→| `PROC foo(POINT p)` (ref) | `func foo(p *POINT)` |
199→| `PROC foo(VAL POINT p)` (val) | `func foo(p POINT)` |
200→
201→Example:
202→```occam
203→RECORD POINT
204→ INT x:
205→ INT y:
206→
207→SEQ
208→ POINT p:
209→ p[x] := 10
210→ p[y] := 20
211→ print.int(p[x] + p[y])
212→```
213→
214→### Arrays
215→
216→| Occam | Go |
217→|-------|-----|
218→| `[5]INT arr:` | `arr := make([]int, 5)` |
219→| `arr[i] := x` | `arr[i] = x` |
220→| `x := arr[i]` | `x = arr[i]` |
221→
222→Example:
223→```occam
224→SEQ
225→ [5]INT arr:
226→ SEQ i = 0 FOR 5
227→ arr[i] := (i + 1) * 10
228→ INT sum:
229→ sum := 0
230→ SEQ i = 0 FOR 5
231→ sum := sum + arr[i]
232→ print.int(sum)
233→```
234→
235→### ALT (Alternation)
236→
237→| Occam | Go |
238→|-------|-----|
239→| `ALT` | `select` |
240→| `guard & c ? x` | Conditional channel with nil pattern |
241→| `SEQ i = 0 FOR n` | `for i := 0; i < n; i++` |
242→| `PAR i = 0 FOR n` | Parallel `for` loop with goroutines |
243→
244→Example:
245→```occam
246→ALT
247→ c1 ? x
248→ print.int(x)
249→ c2 ? y
250→ print.int(y)
251→```
252→
253→Generates:
254→```go
255→select {
256→case x = <-c1:
257→ fmt.Println(x)
258→case y = <-c2:
259→ fmt.Println(y)
260→}
261→```
262→
263→ALT with guards (optional boolean conditions):
264→```occam
265→ALT
266→ enabled & c1 ? x
267→ process(x)
268→ TRUE & c2 ? y
269→ process(y)
270→```
271→
272→### Replicators
273→
274→Replicators allow you to repeat a block of code a specified number of times.
275→
276→| Occam | Go |
277→|-------|-----|
278→| `SEQ i = 0 FOR n` | `for i := 0; i < n; i++` |
279→| `PAR i = 0 FOR n` | Parallel for loop with goroutines |
280→
281→Example with replicated SEQ:
282→```occam
283→SEQ i = 1 FOR 5
284→ print.int(i)
285→```
286→
287→This prints 1, 2, 3, 4, 5.
288→
289→Example with replicated PAR (spawns n concurrent processes):
290→```occam
291→PAR i = 0 FOR 4
292→ c ! i
293→```
294→
295→### Built-in I/O Procedures
296→
297→| Occam | Go |
298→|-------|-----|
299→| `print.int(x)` | `fmt.Println(x)` |
300→| `print.bool(x)` | `fmt.Println(x)` |
301→| `print.string(x)` | `fmt.Println(x)` |
302→| `print.newline()` | `fmt.Println()` |
303→
304→## How Channels are Mapped
305→
306→Both Occam and Go draw from Tony Hoare's Communicating Sequential Processes (CSP) model, making channel communication a natural fit for transpilation.
307→
308→### Conceptual Mapping
309→
310→In Occam, channels are the primary mechanism for communication between parallel processes. A channel is a synchronous, unbuffered, point-to-point connection. Go channels share these characteristics by default.
311→
312→| Concept | Occam | Go |
313→|---------|-------|-----|
314→| Declaration | `CHAN OF INT c:` | `c := make(chan int)` |
315→| Send (blocks until receiver ready) | `c ! value` | `c <- value` |
316→| Receive (blocks until sender ready) | `c ? variable` | `variable = <-c` |
317→| Synchronisation | Implicit in `!` and `?` | Implicit in `<-` |
318→
319→### Synchronous Communication
320→
321→Both languages use synchronous (rendezvous) communication by default:
322→
323→```occam
324→PAR
325→ c ! 42 -- blocks until receiver is ready
326→ c ? x -- blocks until sender is ready
327→```
328→
329→The sender and receiver must both be ready before the communication occurs. This is preserved in the generated Go code, where unbuffered channels have the same semantics.
330→
331→### Differences and Limitations
332→
333→1. **Channel direction**: Occam channels are inherently unidirectional. Go channels can be bidirectional but can be restricted using types (`chan<-` for send-only, `<-chan` for receive-only). The transpiler currently generates bidirectional Go channels.
334→
335→2. **Protocol types**: Simple, sequential, and variant protocols are supported. Nested protocols (protocols referencing other protocols) are not yet supported.
336→
337→3. **Channel arrays**: Channel arrays (`[n]CHAN OF TYPE`) are supported, including indexed send/receive, `[]CHAN OF TYPE` proc params, and ALT with indexed channels.
338→
339→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.
340→
341→## How PAR is Mapped
342→
343→Occam's `PAR` construct runs processes truly in parallel. On the Transputer this was hardware-scheduled; in Go it maps to goroutines coordinated with a `sync.WaitGroup`.
344→
345→### Basic PAR
346→
347→Each branch of a `PAR` block becomes a goroutine. The transpiler inserts a `WaitGroup` to ensure all branches complete before execution continues:
348→
349→```occam
350→PAR
351→ c ! 42
352→ c ? x
353→```
354→
355→Generates:
356→
357→```go
358→var wg sync.WaitGroup
359→wg.Add(2)
360→go func() {
361→ defer wg.Done()
362→ c <- 42
363→}()
364→go func() {
365→ defer wg.Done()
366→ x = <-c
367→}()
368→wg.Wait()
369→```
370→
371→The `wg.Wait()` call blocks until all goroutines have finished, preserving Occam's semantics that execution only continues after all parallel branches complete.
372→
373→### Replicated PAR
374→
375→A replicated `PAR` spawns N concurrent processes using a loop. Each iteration captures the loop variable to avoid closure issues:
376→
377→```occam
378→PAR i = 0 FOR 4
379→ c ! i
380→```
381→
382→Generates:
383→
384→```go
385→var wg sync.WaitGroup
386→wg.Add(int(4))
387→for i := 0; i < 0 + 4; i++ {
388→ i := i // capture loop variable
389→ go func() {
390→ defer wg.Done()
391→ c <- i
392→ }()
393→}
394→wg.Wait()
395→```
396→
397→### Differences and Limitations
398→
399→1. **Scheduling**: Occam on the Transputer had deterministic, priority-based scheduling. Go's goroutine scheduler is preemptive and non-deterministic. Programs that depend on execution order between `PAR` branches may behave differently.
400→
401→2. **Shared memory**: Occam enforces at compile time that parallel processes do not share variables (the "disjointness" rule). The transpiler does not enforce this, so generated Go code may contain data races if the original Occam would have been rejected by a full Occam compiler.
402→
403→3. **PLACED PAR**: Occam's `PLACED PAR` for assigning processes to specific Transputer links or processors is not supported.
404→
405→## How Timers are Mapped
406→
407→Occam's `TIMER` provides access to a hardware clock. The transpiler maps timer operations to Go's `time` package.
408→
409→### Timer Declaration
410→
411→Timer declarations are no-ops in the generated code since Go accesses time through the `time` package directly:
412→
413→```occam
414→TIMER tim:
415→```
416→
417→Generates:
418→
419→```go
420→// TIMER tim
421→```
422→
423→### Reading the Current Time
424→
425→A timer read stores the current time as an integer (microseconds since epoch):
426→
427→```occam
428→TIMER tim:
429→INT t:
430→tim ? t
431→```
432→
433→Generates:
434→
435→```go
436→// TIMER tim
437→var t int
438→t = int(time.Now().UnixMicro())
439→```
440→
441→### Timer Timeouts in ALT
442→
443→Timer cases in ALT allow a process to wait until a deadline. This maps to Go's `time.After` inside a `select`:
444→
445→```occam
446→TIMER tim:
447→INT t:
448→tim ? t
449→ALT
450→ c ? x
451→ process(x)
452→ tim ? AFTER (t + 100000)
453→ handle.timeout()
454→```
455→
456→Generates:
457→
458→```go
459→// TIMER tim
460→var t int
461→t = int(time.Now().UnixMicro())
462→select {
463→case x = <-c:
464→ process(x)
465→case <-time.After(time.Duration((t + 100000) - int(time.Now().UnixMicro())) * time.Microsecond):
466→ handle_timeout()
467→}
468→```
469→
470→The deadline expression `(t + 100000)` represents an absolute time. The generated code computes the remaining duration by subtracting the current time.
471→
472→### AFTER as a Boolean Expression
473→
474→The `AFTER` operator compares two time values and evaluates to `true` if the left operand is later than the right. It maps to `>`:
475→
476→```occam
477→IF
478→ t2 AFTER t1
479→ -- t2 is later
480→```
481→
482→Generates:
483→
484→```go
485→if (t2 > t1) {
486→ // t2 is later
487→}
488→```
489→
490→### Differences and Limitations
491→
492→1. **Clock resolution**: Occam timers are hardware-dependent (often microsecond resolution on the Transputer). The transpiler uses `time.Now().UnixMicro()` for microsecond values, but actual resolution depends on the OS.
493→
494→2. **Guarded timer ALT**: `guard & tim ? AFTER deadline` (timer cases with boolean guards) is not yet supported.
495→
496→3. **Clock wraparound**: Occam's `AFTER` operator handles 32-bit clock wraparound correctly. The transpiler uses a simple `>` comparison, which does not handle wraparound.I need to fix the two places where the argument order is wrong. The issue says the docs show ./occam2go <input-file> -o <output-file> but the code actually implements ./occam2go -o <output-file> <input-file>. I need to fix the docs to match the code.
The incorrect lines are:
- Line 32: ./occam2go examples/print.occ -o output.go
- Line 44: ./occam2go examples/print.occ -o output.go && go run output.go
I can see the issue. Lines 32 and 44 show the input file before -o, but the actual CLI expects -o <output> before the input file. Let me fix both occurrences.
./occam2go examples/print.occ -o output.go
./occam2go -o output.go examples/print.occ
The file /home/david/projects/codeassociates/occam2go/README.md has been updated. All occurrences of './occam2go examples/print.occ -o output.go' were successfully replaced with './occam2go -o output.go examples/print.occ'.
diff --git a/README.md b/README.md index cc992f8..a4d5ece 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Here's how to transpile, compile, and run an Occam program: go build -o occam2go # 2. Transpile an Occam file to Go -./occam2go examples/print.occ -o output.go +./occam2go -o output.go examples/print.occ # 3. Compile the generated Go code go build -o output output.go @@ -41,7 +41,7 @@ go build -o output output.go Or as a one-liner to see the output immediately: ```bash -./occam2go examples/print.occ -o output.go && go run output.go +./occam2go -o output.go examples/print.occ && go run output.go ``` ## Example
git add README.md && git commit -m "$(cat <<'EOF' Fix CLI usage examples to match actual argument order The README showed `./occam2go <input> -o <output>` but the program expects `./occam2go -o <output> <input>`. Fixes #1. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> EOF )"
1 file changed, 2 insertions(+), 2 deletions(-)
Exit code 128 ssh_askpass: exec(/usr/bin/ssh-askpass): No such file or directory git@github.com: Permission denied (publickey). fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists.
origin git@github.com:codeassociates/occam2go.git (fetch) origin git@github.com:codeassociates/occam2go.git (push)
The user doesn't want to proceed with this tool use. The tool use was rejected (eg. if it was a file edit, the new_string was NOT written to the file). STOP what you are doing and wait for the user to tell you how to proceed.
Sounds good — go ahead and push the branch manually. Once it's pushed, let me know and I'll create the PR with gh pr create.
It worked. I think it must have failed for you due to prompting for the ssh passphrase. Anyway, the push has been done please proceed.
gh pr create --title "Fix CLI usage examples to match actual argument order" --body "$(cat <<'EOF' ## Summary - Fix README examples that showed `./occam2go <input> -o <output>` to use the correct order `./occam2go -o <output> <input>` Fixes #1 ## Test plan - [x] Verified the two affected usage examples now match the actual CLI argument order 🤖 Generated with [Claude Code](https://claude.com/claude-code) EOF )"
Thank you. I approved the PR. Are you able to merge it and mark the issue as resolved (close it with a comment resolved by the PR)?
Exit code 1 GraphQL: associate-1 does not have the correct permissions to execute `MergePullRequest` (mergePullRequest)
Looks like the gh CLI token doesn't have permission to merge PRs. You'll need to merge it through the GitHub web UI or update the token permissions. Once you've merged it, I can try closing the issue, or you can do both from the UI — since the PR body has Fixes #1, GitHub will automatically close the issue when the PR is merged.