Skip to content

Commit 3088527

Browse files
authored
Merge pull request #103 from codeassociates/feat/standalone-timer-after-43
Support standalone timer AFTER wait (#43)
2 parents 8555305 + 317238d commit 3088527

4 files changed

Lines changed: 57 additions & 6 deletions

File tree

ast/ast.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,16 @@ type Receive struct {
383383
func (r *Receive) statementNode() {}
384384
func (r *Receive) TokenLiteral() string { return r.Token.Literal }
385385

386+
// TimerAfterWait represents a standalone timer AFTER wait: tim ? AFTER expr
387+
type TimerAfterWait struct {
388+
Token lexer.Token // the ? token
389+
Timer string // timer variable name
390+
Deadline Expression // the deadline expression
391+
}
392+
393+
func (t *TimerAfterWait) statementNode() {}
394+
func (t *TimerAfterWait) TokenLiteral() string { return t.Token.Literal }
395+
386396
// AltBlock represents an ALT block (alternation/select)
387397
// If Replicator is non-nil, this is a replicated ALT (ALT i = 0 FOR n)
388398
type AltBlock struct {

codegen/codegen.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,7 @@ func (g *Generator) containsPrint(stmt ast.Statement) bool {
745745

746746
func (g *Generator) containsTimer(stmt ast.Statement) bool {
747747
switch s := stmt.(type) {
748-
case *ast.TimerDecl, *ast.TimerRead:
748+
case *ast.TimerDecl, *ast.TimerRead, *ast.TimerAfterWait:
749749
return true
750750
case *ast.AltBlock:
751751
for _, c := range s.Cases {
@@ -1135,6 +1135,8 @@ func (g *Generator) generateStatement(stmt ast.Statement) {
11351135
g.generateSend(s)
11361136
case *ast.Receive:
11371137
g.generateReceive(s)
1138+
case *ast.TimerAfterWait:
1139+
g.generateTimerAfterWait(s)
11381140
case *ast.SeqBlock:
11391141
g.generateSeqBlock(s)
11401142
case *ast.ParBlock:
@@ -1431,6 +1433,14 @@ func (g *Generator) generateSend(send *ast.Send) {
14311433
g.write("\n")
14321434
}
14331435

1436+
func (g *Generator) generateTimerAfterWait(s *ast.TimerAfterWait) {
1437+
// tim ? AFTER deadline → time.Sleep(time.Duration(deadline - time.Now().UnixMicro()) * time.Microsecond)
1438+
g.builder.WriteString(strings.Repeat("\t", g.indent))
1439+
g.write("time.Sleep(time.Duration(")
1440+
g.generateExpression(s.Deadline)
1441+
g.write(" - int(time.Now().UnixMicro())) * time.Microsecond)\n")
1442+
}
1443+
14341444
func (g *Generator) generateReceive(recv *ast.Receive) {
14351445
chanRef := goIdent(recv.Channel)
14361446
if len(recv.ChannelIndices) > 0 {

codegen/e2e_concurrency_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,22 @@ func TestE2E_TimerAltTimeout(t *testing.T) {
181181
}
182182
}
183183

184+
func TestE2E_TimerAfterWait(t *testing.T) {
185+
// Test standalone tim ? AFTER expr (non-ALT timer wait)
186+
occam := `SEQ
187+
TIMER tim:
188+
INT t:
189+
tim ? t
190+
tim ? AFTER (t + 1000)
191+
print.int(42)
192+
`
193+
output := transpileCompileRun(t, occam)
194+
expected := "42\n"
195+
if output != expected {
196+
t.Errorf("expected %q, got %q", expected, output)
197+
}
198+
}
199+
184200
func TestE2E_ChanParam(t *testing.T) {
185201
occam := `PROC sender(CHAN OF INT output)
186202
output ! 42

parser/parser.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,13 +1207,28 @@ func (p *Parser) parseTimerDecl() *ast.TimerDecl {
12071207
return decl
12081208
}
12091209

1210-
func (p *Parser) parseTimerRead() *ast.TimerRead {
1211-
stmt := &ast.TimerRead{
1212-
Timer: p.curToken.Literal,
1213-
}
1210+
func (p *Parser) parseTimerRead() ast.Statement {
1211+
timerName := p.curToken.Literal
12141212

12151213
p.nextToken() // move to ?
1216-
stmt.Token = p.curToken
1214+
recvToken := p.curToken
1215+
1216+
// Check for timer AFTER wait: tim ? AFTER expr
1217+
if p.peekTokenIs(lexer.AFTER) {
1218+
p.nextToken() // move to AFTER
1219+
p.nextToken() // move past AFTER to deadline expression
1220+
deadline := p.parseExpression(LOWEST)
1221+
return &ast.TimerAfterWait{
1222+
Token: recvToken,
1223+
Timer: timerName,
1224+
Deadline: deadline,
1225+
}
1226+
}
1227+
1228+
stmt := &ast.TimerRead{
1229+
Timer: timerName,
1230+
}
1231+
stmt.Token = recvToken
12171232

12181233
if !p.expectPeek(lexer.IDENT) {
12191234
return nil

0 commit comments

Comments
 (0)