From 0e3c4e17e9905dfd6d84c6195229375cab69c12c Mon Sep 17 00:00:00 2001 From: Associate 1 Date: Wed, 4 Mar 2026 09:00:21 -0700 Subject: [PATCH] Support SIZE of direction-annotated channel name (#63) Strip trailing ? or ! direction suffix when parsing SIZE operand, so `SIZE monitor?` correctly transpiles to `len(monitor)`. Co-Authored-By: Claude Opus 4.6 --- codegen/e2e_array_test.go | 21 +++++++++++++++++++++ parser/parser.go | 4 ++++ parser/parser_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/codegen/e2e_array_test.go b/codegen/e2e_array_test.go index 9994df1..cf8f287 100644 --- a/codegen/e2e_array_test.go +++ b/codegen/e2e_array_test.go @@ -154,6 +154,27 @@ func TestE2E_SizeArray(t *testing.T) { } } +func TestE2E_SizeDirAnnotated(t *testing.T) { + occam := `PROC bar([]CHAN OF INT out!) + SEQ i = 0 FOR SIZE out! + out[i] ! i +SEQ + [3]CHAN OF INT cs: + PAR + bar(cs) + SEQ + INT x: + SEQ i = 0 FOR 3 + cs[i] ? x + print.int(x) +` + output := transpileCompileRun(t, occam) + expected := "0\n1\n2\n" + if output != expected { + t.Errorf("expected %q, got %q", expected, output) + } +} + func TestE2E_SizeString(t *testing.T) { occam := `SEQ INT n: diff --git a/parser/parser.go b/parser/parser.go index b12426d..020862b 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -2925,6 +2925,10 @@ func (p *Parser) parseExpression(precedence int) ast.Expression { Token: token, Expr: p.parseExpression(PREFIX), } + // Strip trailing direction annotation (e.g., SIZE monitor?) + if p.peekTokenIs(lexer.SEND) || p.peekTokenIs(lexer.RECEIVE) { + p.nextToken() + } case lexer.MOSTNEG_KW, lexer.MOSTPOS_KW: token := p.curToken isNeg := token.Type == lexer.MOSTNEG_KW diff --git a/parser/parser_test.go b/parser/parser_test.go index 570b693..bf9b7ac 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -2386,6 +2386,44 @@ func TestSizeExpression(t *testing.T) { } } +func TestSizeExpressionWithDirection(t *testing.T) { + tests := []struct { + input string + dir string + }{ + {`x := SIZE monitor?` + "\n", "?"}, + {`x := SIZE out!` + "\n", "!"}, + } + for _, tt := range tests { + l := lexer.New(tt.input) + p := New(l) + program := p.ParseProgram() + checkParserErrors(t, p) + + if len(program.Statements) != 1 { + t.Fatalf("input %q: expected 1 statement, got %d", tt.input, len(program.Statements)) + } + + assign, ok := program.Statements[0].(*ast.Assignment) + if !ok { + t.Fatalf("input %q: expected Assignment, got %T", tt.input, program.Statements[0]) + } + + sizeExpr, ok := assign.Value.(*ast.SizeExpr) + if !ok { + t.Fatalf("input %q: expected SizeExpr, got %T", tt.input, assign.Value) + } + + ident, ok := sizeExpr.Expr.(*ast.Identifier) + if !ok { + t.Fatalf("input %q: expected Identifier inside SizeExpr, got %T", tt.input, sizeExpr.Expr) + } + if ident.Value != "monitor" && ident.Value != "out" { + t.Errorf("input %q: unexpected identifier %s", tt.input, ident.Value) + } + } +} + func TestSizeExpressionInBinaryExpr(t *testing.T) { // SIZE has PREFIX precedence, so "SIZE arr + 1" parses as "(SIZE arr) + 1" input := `x := SIZE arr + 1