Add structured coroutines and explicit error handling

This commit is contained in:
pavel 2026-08-27 16:26:40 +02:00
commit fe62e81152
31 changed files with 1701 additions and 325 deletions

View file

@ -137,7 +137,7 @@ func (p *parser) parseProgram() (*Program, error) {
return nil, err
}
prog.Workers = append(prog.Workers, decl)
case p.check(tokenFun):
case p.check(tokenFun) || p.check(tokenSuspend):
fn, err := p.parseFunction()
if err != nil {
return nil, err
@ -320,7 +320,7 @@ func (p *parser) parseClass() (ClassDecl, error) {
var methods []FunctionDecl
for !p.check(tokenRBrace) && !p.check(tokenEOF) {
p.match(tokenOverride)
if !p.check(tokenFun) {
if !p.check(tokenFun) && !p.check(tokenSuspend) {
tok := p.peek()
return ClassDecl{}, fmt.Errorf("expected class member at %d, found %q", tok.pos, tok.lexeme)
}
@ -538,10 +538,12 @@ func (p *parser) parseFunction() (FunctionDecl, error) {
Params: signature.Params,
ReturnType: signature.ReturnType,
Body: body,
Suspend: signature.Suspend,
}, nil
}
func (p *parser) parseFunctionSignature() (FunctionSignature, error) {
suspend := p.match(tokenSuspend)
if _, err := p.expect(tokenFun, "expected 'fun'"); err != nil {
return FunctionSignature{}, err
}
@ -573,6 +575,7 @@ func (p *parser) parseFunctionSignature() (FunctionSignature, error) {
Name: name.lexeme,
Params: params,
ReturnType: returnType,
Suspend: suspend,
}, nil
}
@ -620,6 +623,9 @@ func (p *parser) parseBlock() ([]Stmt, error) {
}
func (p *parser) parseStmt() (Stmt, error) {
if p.check(tokenIdent) && p.peek().lexeme == "go" {
return nil, fmt.Errorf("bare go is removed; use launch inside a coroutine scope")
}
switch {
case p.match(tokenVal):
return p.parseVarDecl(false)
@ -1105,6 +1111,16 @@ func (p *parser) parsePostfix(expr Expr) (Expr, error) {
return nil, fmt.Errorf("expected selector name at %d, found %q", name.pos, name.lexeme)
}
expr = SelectorExpr{Receiver: expr, Name: name.lexeme}
case p.match(tokenSafeDot):
name := p.advance()
if !selectorName(name.lexeme) {
return nil, fmt.Errorf("expected selector name at %d, found %q", name.pos, name.lexeme)
}
expr = SafeSelectorExpr{Receiver: expr, Name: name.lexeme}
case p.match(tokenDoubleBang):
expr = NonNullExpr{Value: expr}
case p.match(tokenQuestion):
expr = TryExpr{Value: expr}
case p.match(tokenLBracket):
index, err := p.parseExpr(0)
if err != nil {
@ -1122,6 +1138,10 @@ func (p *parser) parsePostfix(expr Expr) (Expr, error) {
if !hasTypeArgs {
return expr, nil
}
if p.check(tokenLBrace) {
expr = CallExpr{Callee: expr, TypeArgs: typeArgs}
continue
}
if _, err := p.expect(tokenLParen, "expected '(' after generic type arguments"); err != nil {
return nil, err
}
@ -1149,6 +1169,8 @@ func (p *parser) parsePostfix(expr Expr) (Expr, error) {
call = current
case SelectorExpr:
call = CallExpr{Callee: current}
case IdentExpr:
call = CallExpr{Callee: current}
default:
return expr, nil
}
@ -1236,7 +1258,7 @@ func (p *parser) tryParseCallTypeArgs() ([]string, bool, error) {
p.pos = saved
return nil, false, nil
}
if !p.check(tokenLParen) {
if !p.check(tokenLParen) && !p.check(tokenLBrace) {
p.pos = saved
return nil, false, nil
}