package parser
import (
"math/big"
"strconv"
"strings"
"github.com/marzeq/qk/shared"
"github.com/marzeq/qk/tokeniser"
)
func (p *Parser) ParseExpression() (ExpressionNode, error) {
return p.ParseLogicalOr()
}
func (p *Parser) ParseWholeExpression() (ExpressionNode, error) {
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
expr, err := p.ParseExpression()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Match(tokeniser.TokenEof) {
return nil, shared.NewError(p.CurrLoc(), "unexpected token %s after expression", p.Peek())
}
return expr, nil
}
func (p *Parser) ParseSizeOfExpression() (ExpressionNode, error) {
beginLoc := p.CurrLoc()
if !p.ConsumeBuiltin("sizeof") {
return nil, shared.NewError(p.CurrLoc(), "expected '@sizeof' builtin")
}
if !p.Expect(tokeniser.TokenOpenParen) {
return nil, shared.NewError(p.PrevLoc(), "expected '(' after '@sizeof'")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
var node Node
var err error
p.PushPos()
node, err = p.ParseType()
if err == nil {
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
}
if err != nil || !p.Match(tokeniser.TokenCloseParen) {
p.PopPos()
node, err = p.ParseExpression()
if err != nil {
return nil, shared.NewError(p.PrevLoc(), "expected type or expression after '@sizeof'")
}
} else {
p.CommitPos()
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Expect(tokeniser.TokenCloseParen) {
return nil, shared.NewError(p.PrevLoc(), "expected ')' after '@sizeof' operand")
}
switch node := node.(type) {
case *NamedTypeNode:
// A bare name can denote either a type or a value. Semantic resolution
// chooses the visible binding without evaluating the value expression.
return &SizeOfNode{
Operand: node, Expression: &IdentifierNode{Name: node.Name, Module: node.ModName, Loc: node.Loc},
Loc: p.SpanFrom(beginLoc),
}, nil
case TypeNode:
return &SizeOfNode{
Operand: node,
Loc: p.SpanFrom(beginLoc),
}, nil
case ExpressionNode:
return &SizeOfExprNode{
Operand: node,
Loc: p.SpanFrom(beginLoc),
}, nil
}
panic("unreachable")
}
func (p *Parser) ParseAlignOfExpression() (ExpressionNode, error) {
beginLoc := p.CurrLoc()
p.ConsumeBuiltin("alignof")
if !p.Expect(tokeniser.TokenOpenParen) {
return nil, shared.NewError(p.PrevLoc(), "expected '(' after '@alignof'")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
var operand Node
p.PushPos()
operand, err := p.ParseType()
if err == nil {
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
}
if err != nil || !p.Match(tokeniser.TokenCloseParen) {
p.PopPos()
operand, err = p.ParseExpression()
if err != nil {
return nil, shared.NewError(p.PrevLoc(), "expected type or expression after '@alignof('")
}
} else {
p.CommitPos()
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Expect(tokeniser.TokenCloseParen) {
return nil, shared.NewError(p.PrevLoc(), "expected ')' after '@alignof' operand")
}
node := &AlignOfNode{Loc: p.SpanFrom(beginLoc)}
switch operand := operand.(type) {
case *NamedTypeNode:
// A bare name can denote either a type or a value. Semantic resolution
// chooses the visible binding without evaluating the value expression.
node.Operand = operand
node.Expression = &IdentifierNode{Name: operand.Name, Module: operand.ModName, Loc: operand.Loc}
case TypeNode:
node.Operand = operand
case ExpressionNode:
node.Expression = operand
default:
panic("unreachable")
}
return node, nil
}
func (p *Parser) ParseOffsetOfExpression() (ExpressionNode, error) {
beginLoc := p.CurrLoc()
p.ConsumeBuiltin("offsetof")
if !p.Expect(tokeniser.TokenOpenParen) {
return nil, shared.NewError(p.PrevLoc(), "expected '(' after '@offsetof'")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
operand, err := p.ParseType()
if err != nil {
return nil, shared.NewError(p.PrevLoc(), "expected type after '@offsetof('")
}
if !p.Expect(tokeniser.TokenComma) {
return nil, shared.NewError(p.PrevLoc(), "expected ',' after type in '@offsetof'")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
field, ok := p.ExpectGet(tokeniser.TokenIdentifier)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected field name in '@offsetof'")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Expect(tokeniser.TokenCloseParen) {
return nil, shared.NewError(p.PrevLoc(), "expected ')' after '@offsetof' field")
}
return &OffsetOfNode{Operand: operand, Field: field.Value, Loc: p.SpanFrom(beginLoc)}, nil
}
func (p *Parser) ParseLogicalOr() (ExpressionNode, error) {
beginLoc := p.CurrLoc()
left, err := p.ParseLogicalAnd()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TokenLogicalOr) {
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
right, err := p.ParseLogicalAnd()
if err != nil {
return nil, err
}
left = &BinaryOpNode{
Op: BinaryOpLogicalOr,
Operand1: left,
Operand2: right,
Loc: p.SpanFrom(beginLoc),
}
}
return left, nil
}
func (p *Parser) ParseLogicalAnd() (ExpressionNode, error) {
beginLoc := p.CurrLoc()
left, err := p.ParseBitwiseOr()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TokenLogicalAnd) {
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
right, err := p.ParseBitwiseOr()
if err != nil {
return nil, err
}
left = &BinaryOpNode{
Op: BinaryOpLogicalAnd,
Operand1: left,
Operand2: right,
Loc: p.SpanFrom(beginLoc),
}
}
return left, nil
}
func (p *Parser) ParseBitwiseOr() (ExpressionNode, error) {
return p.parseLeftAssociative(p.ParseBitwiseXor, []tokeniser.TokenKind{tokeniser.TokenPipe}, map[tokeniser.TokenKind]BinaryOpKind{
tokeniser.TokenPipe: BinaryOpBitwiseOr,
})
}
func (p *Parser) ParseBitwiseXor() (ExpressionNode, error) {
return p.parseLeftAssociative(p.ParseBitwiseAnd, []tokeniser.TokenKind{tokeniser.TokenCaret}, map[tokeniser.TokenKind]BinaryOpKind{
tokeniser.TokenCaret: BinaryOpBitwiseXor,
})
}
func (p *Parser) ParseBitwiseAnd() (ExpressionNode, error) {
return p.parseLeftAssociative(p.ParseComparison, []tokeniser.TokenKind{tokeniser.TokenAmpersand}, map[tokeniser.TokenKind]BinaryOpKind{
tokeniser.TokenAmpersand: BinaryOpBitwiseAnd,
})
}
func (p *Parser) parseLeftAssociative(
parseOperand func() (ExpressionNode, error),
tokens []tokeniser.TokenKind,
operators map[tokeniser.TokenKind]BinaryOpKind,
) (ExpressionNode, error) {
beginLoc := p.CurrLoc()
left, err := parseOperand()
if err != nil {
return nil, err
}
for p.Match(tokens...) {
op := operators[p.Consume().Type]
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
right, err := parseOperand()
if err != nil {
return nil, err
}
left = &BinaryOpNode{Op: op, Operand1: left, Operand2: right, Loc: p.SpanFrom(beginLoc)}
}
return left, nil
}
func (p *Parser) ParseUnary() (ExpressionNode, error) {
beginLoc := p.CurrLoc()
if p.MatchAdjacentPair(tokeniser.TokenPlus) {
return nil, shared.NewError(beginLoc, "use ... += 1 instead")
}
if p.MatchAdjacentPair(tokeniser.TokenMinus) {
return nil, shared.NewError(beginLoc, "use ... -= 1 instead")
}
if p.Match(tokeniser.TokenExclam, tokeniser.TokenMinus, tokeniser.TokenTilde) {
op := p.Consume()
var val UnaryOpKind
switch op.Type {
case tokeniser.TokenExclam:
val = UnaryOpLogicalNot
case tokeniser.TokenMinus:
val = UnaryOpNegate
case tokeniser.TokenTilde:
val = UnaryOpBitwiseNot
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
expr, err := p.ParseUnary()
if err != nil {
return nil, err
}
return &UnaryOpNode{
Op: val,
Operand: expr,
Loc: p.SpanFrom(beginLoc),
}, nil
}
return p.ParsePostfix()
}
func (p *Parser) ParsePostfix() (ExpressionNode, error) {
beginLoc := p.CurrLoc()
expr, err := p.ParseTerm()
if err != nil {
return nil, err
}
for {
switch {
case p.Match(tokeniser.TokenDot) && p.Next().Type == tokeniser.TokenOpenCurly:
qualified, ok := expr.(*IdentifierNode)
if !ok {
qualified, ok = dottedIdentifier(expr)
}
if !ok {
return expr, nil
}
p.Inc()
return p.ParseStructLiteral(qualified)
case p.Match(tokeniser.TokenOpenParen):
// A type-producing binding uses ordinary call syntax. Before a
// structural literal the following '.{' makes that interpretation
// unambiguous, so retain the arguments on the type identifier.
p.PushPos()
typeArguments, typeErr := p.parseCallTypeArguments()
if typeErr == nil && p.Match(tokeniser.TokenDot) && p.Next().Type == tokeniser.TokenOpenCurly {
qualified, ok := expr.(*IdentifierNode)
if !ok {
qualified, ok = dottedIdentifier(expr)
}
if ok {
p.CommitPos()
qualified.TypeArguments = typeArguments
qualified.Loc = p.SpanFrom(qualified.Loc)
expr = qualified
continue
}
}
p.PopPos()
call, err := p.ParseCall(expr)
if err != nil {
return nil, err
}
if identifier, ok := expr.(*IdentifierNode); ok {
call.Name = identifier
}
expr = call
case p.MatchAdjacentPair(tokeniser.TokenPlus):
return nil, shared.NewError(p.CurrLoc(), "use ... += 1 instead")
case p.MatchAdjacentPair(tokeniser.TokenMinus):
return nil, shared.NewError(p.CurrLoc(), "use ... -= 1 instead")
case p.Match(tokeniser.TokenOpenSquare):
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
var first ExpressionNode
if !p.Match(tokeniser.TokenColon) {
var err error
first, err = p.ParseExpression()
if err != nil {
return nil, err
}
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenColon) {
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
var end ExpressionNode
if !p.Match(tokeniser.TokenCloseSquare) {
var err error
end, err = p.ParseExpression()
if err != nil {
return nil, err
}
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Expect(tokeniser.TokenCloseSquare) {
return nil, shared.NewError(p.PrevLoc(), "expected ']'")
}
expr = &SliceExprNode{
Subject: expr,
Start: first,
End: end,
Loc: p.SpanFrom(beginLoc),
}
continue
}
if first == nil {
return nil, shared.NewError(p.CurrLoc(), "expected index or ':'")
}
if !p.Expect(tokeniser.TokenCloseSquare) {
return nil, shared.NewError(p.PrevLoc(), "expected ']'")
}
expr = &IndexExprNode{
Subject: expr,
Index: first,
Loc: p.SpanFrom(beginLoc),
}
case p.Match(tokeniser.TokenDot):
p.Inc()
if p.Match(tokeniser.TokenAsterisk) {
p.Inc()
expr = &UnaryOpNode{
Op: UnaryOpDereference,
Operand: expr,
Loc: p.SpanFrom(beginLoc),
}
continue
}
if p.Match(tokeniser.TokenAmpersand) {
p.Inc()
op := UnaryOpReference
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordMut) {
p.Inc()
op = UnaryOpMutableReference
}
expr = &UnaryOpNode{
Op: op,
Operand: expr,
Loc: p.SpanFrom(beginLoc),
}
continue
}
if p.Match(tokeniser.TokenOpenParen) {
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
typ, err := p.ParseType()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Expect(tokeniser.TokenCloseParen) {
return nil, shared.NewError(p.PrevLoc(), "expected ')'")
}
expr = &CastNode{
ToType: typ,
Operand: expr,
Loc: p.SpanFrom(beginLoc),
}
continue
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
field, err := p.ParseIdent()
if err != nil {
return nil, err
}
expr = &FieldAccessNode{
Subject: expr,
Field: field,
Loc: p.SpanFrom(beginLoc),
}
default:
return expr, nil
}
}
}
func dottedIdentifier(expr ExpressionNode) (*IdentifierNode, bool) {
var parts []string
var typeArguments []TypeNode
for {
switch n := expr.(type) {
case *FieldAccessNode:
if len(parts) == 0 {
typeArguments = n.Field.TypeArguments
}
parts = append([]string{n.Field.Name}, parts...)
expr = n.Subject
case *IdentifierNode:
parts = append([]string{n.Name}, parts...)
if len(parts) < 2 {
return nil, false
}
return &IdentifierNode{
Name: parts[len(parts)-1], Module: strings.Join(parts[:len(parts)-1], "."),
TypeArguments: typeArguments, Loc: n.Loc,
}, true
default:
return nil, false
}
}
}
func (p *Parser) ParseComparison() (ExpressionNode, error) {
beginLoc := p.CurrLoc()
left, err := p.ParseShift()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TokenEqualsEquals, tokeniser.TokenNotEquals,
tokeniser.TokenLess, tokeniser.TokenGreater, tokeniser.TokenLessEquals, tokeniser.TokenGreaterEquals) {
op := p.Consume()
var val BinaryOpKind
switch op.Type {
case tokeniser.TokenEqualsEquals:
val = BinaryOpEqual
case tokeniser.TokenNotEquals:
val = BinaryOpNotEqual
case tokeniser.TokenLess:
val = BinaryOpLess
case tokeniser.TokenGreater:
val = BinaryOpGreater
case tokeniser.TokenLessEquals:
val = BinaryOpLessEqual
case tokeniser.TokenGreaterEquals:
val = BinaryOpGreaterEqual
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
right, err := p.ParseShift()
if err != nil {
return nil, err
}
left = &BinaryOpNode{
Op: val,
Operand1: left,
Operand2: right,
Loc: p.SpanFrom(beginLoc),
}
}
return left, nil
}
func (p *Parser) ParseShift() (ExpressionNode, error) {
return p.parseLeftAssociative(p.ParseAddSub,
[]tokeniser.TokenKind{tokeniser.TokenShiftLeft, tokeniser.TokenShiftRight},
map[tokeniser.TokenKind]BinaryOpKind{
tokeniser.TokenShiftLeft: BinaryOpShiftLeft,
tokeniser.TokenShiftRight: BinaryOpShiftRight,
})
}
func (p *Parser) ParseAddSub() (ExpressionNode, error) {
beginLoc := p.CurrLoc()
left, err := p.ParseMulDiv()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TokenPlus, tokeniser.TokenMinus) {
op := p.Consume()
var val BinaryOpKind
if op.Type == tokeniser.TokenPlus {
val = BinaryOpAdd
} else {
val = BinaryOpSubtract
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
right, err := p.ParseMulDiv()
if err != nil {
return nil, err
}
left = &BinaryOpNode{
Op: val,
Operand1: left,
Operand2: right,
Loc: p.SpanFrom(beginLoc),
}
}
return left, nil
}
func (p *Parser) ParseMulDiv() (ExpressionNode, error) {
beginLoc := p.CurrLoc()
left, err := p.ParseUnary()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TokenAsterisk, tokeniser.TokenSlash, tokeniser.TokenPercent) {
op := p.Consume()
var val BinaryOpKind
switch op.Type {
case tokeniser.TokenAsterisk:
val = BinaryOpMultiply
case tokeniser.TokenSlash:
val = BinaryOpDivide
case tokeniser.TokenPercent:
val = BinaryOpModulo
default:
return nil, shared.NewError(p.PrevLoc(), "unexpected operator %s", op)
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
right, err := p.ParseUnary()
if err != nil {
return nil, err
}
left = &BinaryOpNode{
Op: val,
Operand1: left,
Operand2: right,
Loc: p.SpanFrom(beginLoc),
}
}
return left, nil
}
func (p *Parser) ParseTerm() (ExpressionNode, error) {
beginLoc := p.CurrLoc()
if p.Match(tokeniser.TokenPipe, tokeniser.TokenLogicalOr) {
return p.ParseLambdaExpression()
}
if p.Match(tokeniser.TokenDot) && p.Next().Type == tokeniser.TokenOpenCurly {
p.Inc()
return p.ParseStructLiteral(nil)
}
if p.Match(tokeniser.TokenDot) && p.Next().Type == tokeniser.TokenIdentifier {
p.Inc()
variant := p.Consume()
return &EnumLiteralNode{Variant: variant.Value, Loc: p.SpanFrom(beginLoc)}, nil
}
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordIf) {
expr, err := p.ParseIfExpression()
if err != nil {
return nil, err
}
return expr, nil
}
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordWhen) {
node, err := p.parseWhen(WhenExpression)
if err != nil {
return nil, err
}
return node, nil
}
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordMatch) {
return p.ParseMatch(true)
}
if p.Match(tokeniser.TokenOpenCurly) {
expr, err := p.ParseBlockExpression()
if err != nil {
return nil, err
}
return expr, nil
}
if p.MatchBuiltin("sizeof") {
expr, err := p.ParseSizeOfExpression()
if err != nil {
return nil, err
}
return expr, nil
}
if p.MatchBuiltin("alignof") {
return p.ParseAlignOfExpression()
}
if p.MatchBuiltin("offsetof") {
return p.ParseOffsetOfExpression()
}
if p.MatchBuiltin("embed") {
return p.ParseEmbedExpression()
}
if p.MatchBuiltin("asm") {
return p.ParseInlineAsmExpression()
}
if p.MatchBuiltin("repr") {
begin := p.CurrLoc()
p.ConsumeBuiltin("repr")
if !p.Expect(tokeniser.TokenOpenParen) {
return nil, shared.NewError(p.PrevLoc(), "expected '(' after '@repr'")
}
operand, err := p.ParseExpression()
if err != nil {
return nil, err
}
if !p.Expect(tokeniser.TokenCloseParen) {
return nil, shared.NewError(p.PrevLoc(), "expected ')' after '@repr' operand")
}
return &ReprNode{Operand: operand, Loc: p.SpanFrom(begin)}, nil
}
if p.Match(tokeniser.TokenOpenParen) {
p.Consume()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
expr, err := p.ParseExpression()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Expect(tokeniser.TokenCloseParen) {
return nil, shared.NewError(p.PrevLoc(), "expected ')'")
}
return expr, nil
}
if p.Match(tokeniser.TokenNoInitializer) {
p.Inc()
return &NoInitializerNode{Loc: p.SpanFrom(beginLoc)}, nil
}
if p.Match(tokeniser.TokenIdentifier) {
ident, err := p.ParseIdent()
if err != nil {
return nil, err
}
return ident, nil
}
if p.MatchBuiltin("len") {
p.ConsumeBuiltin("len")
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Expect(tokeniser.TokenOpenParen) {
return nil, shared.NewError(p.PrevLoc(), "expected '(' after '@len'")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
expr, err := p.ParseExpression()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Expect(tokeniser.TokenCloseParen) {
return nil, shared.NewError(p.PrevLoc(), "expected ')' after '@len' expression")
}
return &UnaryOpNode{
Op: UnaryOpSliceLen,
Operand: expr,
Loc: p.SpanFrom(beginLoc),
}, nil
}
if p.Match(tokeniser.TokenOpenSquare) {
return p.ParseSliceLiteral()
}
if p.Match(tokeniser.TokenKeyword) {
val := p.Peek().Value
switch val {
case string(tokeniser.KeywordTrue), string(tokeniser.KeywordFalse):
bLit := p.Consume()
return &BoolLiteralNode{
Value: bLit.Value,
Loc: p.SpanFrom(beginLoc),
}, nil
case string(tokeniser.KeywordNil):
p.Inc()
return &NilLiteralNode{
Loc: p.SpanFrom(beginLoc),
}, nil
}
}
if p.Match(tokeniser.TokenNumber) {
nLit := p.Consume()
return &IntegerLiteralNode{
Value: nLit.Value,
Loc: p.SpanFrom(beginLoc),
}, nil
}
if p.Match(tokeniser.TokenFloat) {
nLit := p.Consume()
return &FloatLiteralNode{
Value: nLit.Value,
Loc: p.SpanFrom(beginLoc),
}, nil
}
if p.Match(tokeniser.TokenChar) {
cLit := p.Consume()
return &CharLiteralNode{
Value: []byte(cLit.Value)[0],
Loc: p.SpanFrom(beginLoc),
}, nil
}
if p.Match(tokeniser.TokenString) {
sLit := p.Consume()
return &StringLiteralNode{
Value: sLit.Value,
Loc: p.SpanFrom(beginLoc),
}, nil
}
if p.Match(tokeniser.TokenCString) {
literal := p.Consume()
return &CStringLiteralNode{Value: literal.Value, Loc: p.SpanFrom(beginLoc)}, nil
}
return nil, shared.NewError(p.CurrLoc(), "unexpected token %s", p.Peek())
}
func (p *Parser) ParseLambdaExpression() (*LambdaNode, error) {
beginLoc := p.CurrLoc()
var args []*FunctionNodeArg
typedVariadic := false
if p.Match(tokeniser.TokenLogicalOr) {
p.Inc()
} else {
p.Inc() // opening '|'
for {
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenPipe) {
p.Inc()
break
}
name, ok := p.ExpectGet(tokeniser.TokenIdentifier)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected lambda parameter name or '|'")
}
group := []*FunctionNodeArg{{Name: name.Value, Loc: name.Loc}}
if p.Match(tokeniser.TokenEquals) {
return nil, shared.NewError(p.CurrLoc(), "lambda parameters cannot have default values")
}
for !p.Match(tokeniser.TokenColon, tokeniser.TokenPipe) {
if !p.Expect(tokeniser.TokenComma) {
return nil, shared.NewError(p.PrevLoc(), "expected ':', ',' or '|' after lambda parameter")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenPipe) {
break
}
name, ok = p.ExpectGet(tokeniser.TokenIdentifier)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected lambda parameter name")
}
group = append(group, &FunctionNodeArg{Name: name.Value, Loc: name.Loc})
if p.Match(tokeniser.TokenEquals) {
return nil, shared.NewError(p.CurrLoc(), "lambda parameters cannot have default values")
}
}
if p.Match(tokeniser.TokenColon) {
p.Inc()
isTypedVariadic := p.Match(tokeniser.Token3Dots)
if isTypedVariadic {
if len(group) != 1 {
return nil, shared.NewError(p.CurrLoc(), "typed variadic parameter cannot use a grouped declaration")
}
p.Inc()
}
typeNode, err := p.ParseType()
if err != nil {
return nil, err
}
if isTypedVariadic {
typeNode = &SliceTypeNode{ElementType: typeNode, Loc: typeNode.GetLoc()}
typedVariadic = true
}
for _, arg := range group {
arg.Type = typeNode
}
}
if p.Match(tokeniser.TokenEquals) {
return nil, shared.NewError(p.CurrLoc(), "lambda parameters cannot have default values")
}
args = append(args, group...)
if typedVariadic {
if p.Match(tokeniser.TokenComma) {
return nil, shared.NewError(p.CurrLoc(), "typed variadic parameter must be last")
}
if !p.Expect(tokeniser.TokenPipe) {
return nil, shared.NewError(p.PrevLoc(), "expected '|' after lambda parameters")
}
break
}
if p.Match(tokeniser.TokenComma) {
p.Inc()
continue
}
if !p.Expect(tokeniser.TokenPipe) {
return nil, shared.NewError(p.PrevLoc(), "expected ',' or '|' after lambda parameter")
}
break
}
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Expect(tokeniser.TokenFatArrow) {
return nil, shared.NewError(p.PrevLoc(), "expected '=>' after lambda parameters")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
var body ExpressionNode
var err error
if p.Match(tokeniser.TokenOpenCurly) {
body, err = p.ParseBlockExpression()
} else {
body, err = p.ParseExpression()
}
if err != nil {
return nil, err
}
return &LambdaNode{Args: args, Body: body, TypedVariadic: typedVariadic, Loc: p.SpanFrom(beginLoc)}, nil
}
func (p *Parser) ParseEmbedExpression() (*EmbedNode, error) {
begin := p.CurrLoc()
p.ConsumeBuiltin("embed")
if !p.Expect(tokeniser.TokenOpenParen) {
return nil, shared.NewError(p.PrevLoc(), "expected '(' after '@embed'")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Match(tokeniser.TokenString) {
return nil, shared.NewError(p.CurrLoc(), "expected file path string in '@embed'")
}
path := p.Consume().Value
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Expect(tokeniser.TokenCloseParen) {
return nil, shared.NewError(p.PrevLoc(), "expected ')' after '@embed' path")
}
return &EmbedNode{Path: path, Loc: p.SpanFrom(begin)}, nil
}
// ParseInlineAsmExpression parses:
//
// @asm("template",
// out u64 "=r",
// in value "r",
// clobber "cc",
// volatile)
//
// Outputs are intentionally listed before inputs because their positions form
// the leading operands in LLVM's inline-assembly constraint string.
func (p *Parser) ParseInlineAsmExpression() (*InlineAsmNode, error) {
begin := p.CurrLoc()
p.ConsumeBuiltin("asm")
if !p.Expect(tokeniser.TokenOpenParen) {
return nil, shared.NewError(p.PrevLoc(), "expected '(' after '@asm'")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
template, ok := p.ExpectGet(tokeniser.TokenString)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "@asm requires a string literal template")
}
node := &InlineAsmNode{Template: template.Value}
seenInput := false
seenClobber := false
for {
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenCloseParen) {
p.Inc()
break
}
if !p.Expect(tokeniser.TokenComma) {
return nil, shared.NewError(p.PrevLoc(), "expected ',' or ')' in @asm")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenCloseParen) {
p.Inc()
break
}
entryLoc := p.CurrLoc()
if p.Match(tokeniser.TokenIdentifier) && p.Peek().Value == "out" {
if seenInput || seenClobber {
return nil, shared.NewError(entryLoc, "@asm outputs must precede inputs and clobbers")
}
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
typeNode, err := p.ParseType()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
constraint, ok := p.ExpectGet(tokeniser.TokenString)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected output constraint string after @asm output type")
}
node.Outputs = append(node.Outputs, InlineAsmOutput{TypeNode: typeNode, Constraint: constraint.Value, Loc: p.SpanFrom(entryLoc)})
continue
}
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordIn) {
if seenClobber {
return nil, shared.NewError(entryLoc, "@asm inputs must precede clobbers")
}
seenInput = true
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
value, err := p.ParseExpression()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
constraint, ok := p.ExpectGet(tokeniser.TokenString)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected input constraint string after @asm input value")
}
node.Inputs = append(node.Inputs, InlineAsmInput{Value: value, Constraint: constraint.Value, Loc: p.SpanFrom(entryLoc)})
continue
}
if p.Match(tokeniser.TokenIdentifier) && p.Peek().Value == "clobber" {
seenClobber = true
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
name, ok := p.ExpectGet(tokeniser.TokenString)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected clobber name string")
}
node.Clobbers = append(node.Clobbers, name.Value)
continue
}
if p.Match(tokeniser.TokenIdentifier) && p.Peek().Value == "volatile" {
p.Inc()
node.Volatile = true
continue
}
return nil, shared.NewError(entryLoc, "expected 'out', 'in', 'clobber', or 'volatile' in @asm")
}
node.Loc = p.SpanFrom(begin)
return node, nil
}
func (p *Parser) ParseFunctionCall(name *IdentifierNode) (*FunctionCallNode, error) {
call, err := p.ParseCall(name)
if err != nil {
return nil, err
}
call.Name = name
return call, nil
}
func (p *Parser) ParseCall(callee ExpressionNode) (*FunctionCallNode, error) {
var args []ExpressionNode
expanded := false
if !p.Expect(tokeniser.TokenOpenParen) {
return nil, shared.NewError(p.PrevLoc(), "expected '('")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Match(tokeniser.TokenCloseParen) {
for {
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenCloseParen) {
break
}
arg, err := p.ParseExpression()
if err != nil {
return nil, err
}
args = append(args, arg)
if p.Match(tokeniser.Token3Dots) {
p.Inc()
expanded = true
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Match(tokeniser.TokenCloseParen) {
return nil, shared.NewError(p.CurrLoc(), "expanded slice must be the final call argument")
}
break
}
if !p.Match(tokeniser.TokenComma) {
break
}
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
}
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Expect(tokeniser.TokenCloseParen) {
return nil, shared.NewError(p.PrevLoc(), "expected ')'")
}
return &FunctionCallNode{
Callee: callee,
Args: args,
VariadicExpansion: expanded,
Loc: callee.GetLoc().WithEnd(p.PrevLoc()),
}, nil
}
func (p *Parser) ParseStructLiteral(name *IdentifierNode) (*StructLiteralNode, error) {
beginLoc := p.CurrLoc()
if name != nil {
beginLoc = name.GetLoc()
}
if !p.Expect(tokeniser.TokenOpenCurly) {
return nil, shared.NewError(p.PrevLoc(), "expected '{' to start struct literal")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
node := &StructLiteralNode{
Name: name,
Loc: p.SpanFrom(beginLoc),
}
if p.Match(tokeniser.TokenDot) {
for {
if !p.Expect(tokeniser.TokenDot) {
return nil, shared.NewError(p.PrevLoc(), "expected '.' before flag name")
}
member, ok := p.ExpectGet(tokeniser.TokenIdentifier)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected flag name")
}
node.FlagMembers = append(node.FlagMembers, member.Value)
if !p.Match(tokeniser.TokenComma, tokeniser.TokenNewline) {
break
}
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenCloseCurly) {
break
}
}
if !p.Expect(tokeniser.TokenCloseCurly) {
return nil, shared.NewError(p.PrevLoc(), "expected '}' to end flags literal")
}
node.Loc = p.SpanFrom(beginLoc)
return node, nil
}
for {
if p.Match(tokeniser.TokenCloseCurly) {
break
}
if p.Match(tokeniser.TokenNoInitializer) {
node.NoInitRemaining = true
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenComma) {
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
}
if !p.Match(tokeniser.TokenCloseCurly) {
return nil, shared.NewError(p.CurrLoc(), "'---' must be the final struct initializer entry")
}
break
}
fieldName, ok := p.ExpectGet(tokeniser.TokenIdentifier)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected field name")
}
if !p.Expect(tokeniser.TokenEquals) {
return nil, shared.NewError(p.PrevLoc(), "expected '='")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
fieldValue, err := p.ParseExpression()
if err != nil {
return nil, err
}
node.Fields = append(node.Fields, shared.Pair[string, ExpressionNode]{L: fieldName.Value, R: fieldValue})
if !p.Match(tokeniser.TokenComma, tokeniser.TokenNewline) {
break
}
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
}
if !p.Expect(tokeniser.TokenCloseCurly) {
return nil, shared.NewError(p.PrevLoc(), "expected '}' to end struct literal")
}
node.Loc = p.SpanFrom(beginLoc)
return node, nil
}
func (p *Parser) ParseSliceLiteral() (*SliceLiteralNode, error) {
beginLoc := p.CurrLoc()
if !p.Expect(tokeniser.TokenOpenSquare) {
return nil, shared.NewError(p.PrevLoc(), "expected '[' to start slice literal")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
var elements []ExpressionNode
for {
if p.Match(tokeniser.TokenCloseSquare) {
break
}
elem, err := p.ParseExpression()
if err != nil {
return nil, err
}
if len(elements) == 0 && p.Match(tokeniser.TokenSemicolon) {
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
amount, err := p.ParseExpression()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Expect(tokeniser.TokenCloseSquare) {
return nil, shared.NewError(p.PrevLoc(), "expected ']' to end repeated slice literal")
}
return &SliceLiteralNode{
RepeatValue: elem,
RepeatAmount: amount,
Loc: p.SpanFrom(beginLoc),
}, nil
}
elements = append(elements, elem)
if !p.Match(tokeniser.TokenComma, tokeniser.TokenNewline) {
break
}
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
}
if !p.Expect(tokeniser.TokenCloseSquare) {
return nil, shared.NewError(p.PrevLoc(), "expected ']' to end slice literal")
}
return &SliceLiteralNode{
Elements: elements,
Loc: p.SpanFrom(beginLoc),
}, nil
}
func (p *Parser) ParseIdent() (*IdentifierNode, error) {
beginLoc := p.CurrLoc()
firstIdent, ok := p.ExpectGet(tokeniser.TokenIdentifier)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected identifier")
}
node := &IdentifierNode{
Name: firstIdent.Value,
Loc: p.SpanFrom(beginLoc),
}
return node, nil
}
func (p *Parser) ParseType() (TypeNode, error) {
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordWhen) {
return p.parseWhen(WhenType)
}
if p.MatchBuiltin("reprof") {
begin := p.CurrLoc()
p.ConsumeBuiltin("reprof")
if !p.Expect(tokeniser.TokenOpenParen) {
return nil, shared.NewError(p.PrevLoc(), "expected '(' after '@reprof'")
}
operand, err := p.ParseType()
if err != nil {
return nil, err
}
if !p.Expect(tokeniser.TokenCloseParen) {
return nil, shared.NewError(p.PrevLoc(), "expected ')' after '@reprof' operand")
}
return &ReprTypeNode{Operand: operand, Loc: p.SpanFrom(begin)}, nil
}
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordDyn) {
return nil, shared.NewError(p.CurrLoc(), "dynamic trait pointer type must start with '*'")
}
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordMut) &&
p.Next().Type == tokeniser.TokenKeyword && p.Next().Value == string(tokeniser.KeywordDyn) {
return nil, shared.NewError(p.CurrLoc(), "mutable dynamic trait pointer type must start with '*mut'")
}
if p.Match(tokeniser.TokenAsterisk) {
return p.ParsePointerType()
}
if p.Match(tokeniser.TokenOpenSquare) {
return p.ParseArrayOrSliceType()
}
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordStruct) {
return p.ParseStructType()
}
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordEnum) {
return p.ParseEnumType()
}
if p.Match(tokeniser.TokenIdentifier) && p.Peek().Value == "flags" {
if p.Next().Type == tokeniser.TokenOpenParen {
return p.ParseFlagsType()
}
if p.Next().Type == tokeniser.TokenOpenCurly {
return nil, shared.NewError(p.Next().Loc, "expected backing type in parentheses after 'flags'")
}
}
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordUnion) {
return p.ParseUnionType()
}
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordOpaque) {
loc := p.CurrLoc()
p.Inc()
return &OpaqueTypeNode{Loc: loc}, nil
}
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordTrait) {
return p.ParseTraitType()
}
return p.ParseNamedType()
}
func (p *Parser) ParseDynType(begin shared.Location, mutable bool) (*DynTypeNode, error) {
if !p.Expect(tokeniser.TokenKeyword) {
return nil, shared.NewError(p.PrevLoc(), "expected 'dyn' to start dynamic trait type")
}
trait, err := p.ParseNamedType()
if err != nil {
return nil, err
}
return &DynTypeNode{TraitType: trait, Mutable: mutable, Loc: p.SpanFrom(begin)}, nil
}
func (p *Parser) ParseTraitType() (*TraitTypeNode, error) {
begin := p.CurrLoc()
p.Inc()
if !p.Expect(tokeniser.TokenOpenCurly) {
return nil, shared.NewError(p.PrevLoc(), "expected '{' after trait")
}
methods := []TraitMethodNode{}
seen := map[string]struct{}{}
for {
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenCloseCurly) {
p.Inc()
break
}
loc := p.CurrLoc()
kw, ok := p.ExpectGet(tokeniser.TokenKeyword)
if !ok || kw.Value != string(tokeniser.KeywordLet) {
return nil, shared.NewError(p.PrevLoc(), "expected trait method declaration")
}
name, ok := p.ExpectGet(tokeniser.TokenIdentifier)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected trait method name")
}
if _, duplicate := seen[name.Value]; duplicate {
return nil, shared.NewError(name.Loc, "duplicate trait method %q", name.Value)
}
seen[name.Value] = struct{}{}
genericParameters := []GenericParameterNode{}
if !p.Expect(tokeniser.TokenOpenParen) {
return nil, shared.NewError(p.PrevLoc(), "expected '('")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
receiver := MethodReceiverNone
if p.Match(tokeniser.TokenIdentifier) && p.Peek().Value == "self" {
p.Inc()
receiver = MethodReceiverValue
} else if p.Match(tokeniser.TokenAsterisk) {
p.Inc()
receiver = MethodReceiverPointer
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordMut) {
p.Inc()
receiver = MethodReceiverMutablePointer
}
self, ok := p.ExpectGet(tokeniser.TokenIdentifier)
if !ok || self.Value != "self" {
return nil, shared.NewError(p.PrevLoc(), "trait method receiver must be self, *self, or *mut self")
}
} else {
return nil, shared.NewError(p.CurrLoc(), "trait method must have a self, *self, or *mut self receiver")
}
args := []*FunctionNodeArg{}
if p.Match(tokeniser.TokenComma) {
p.Inc()
}
for {
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenCloseParen) {
break
}
if parameter, matched, parseErr := p.parseTypeParameter(); matched || parseErr != nil {
if parseErr != nil {
return nil, parseErr
}
genericParameters = append(genericParameters, parameter)
if p.Match(tokeniser.TokenComma) {
p.Inc()
}
continue
}
arg, err := p.ParseIdent()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Expect(tokeniser.TokenColon) {
return nil, shared.NewError(
p.PrevLoc(),
"expected ':' after trait method parameter",
)
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
t, err := p.ParseType()
if err != nil {
return nil, err
}
args = append(args, &FunctionNodeArg{
Name: arg.Name,
Type: t,
Loc: arg.Loc,
})
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Match(tokeniser.TokenComma) {
break
}
p.Inc()
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Expect(tokeniser.TokenCloseParen) {
return nil, shared.NewError(p.PrevLoc(), "expected ')'")
}
var ret TypeNode = &NamedTypeNode{Name: "void", Loc: loc}
if p.Match(tokeniser.TokenColon) {
p.Inc()
var err error
ret, err = p.parseFunctionReturnType()
if err != nil {
return nil, err
}
}
var body Node
var err error
expressionBody := false
if p.Match(tokeniser.TokenOpenCurly) {
body, err = p.ParseBlock()
if err != nil {
return nil, err
}
} else if p.Match(tokeniser.TokenEquals) {
p.Inc()
expressionBody = true
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenOpenCurly) {
body, err = p.ParseBlockExpression()
} else {
body, err = p.ParseExpression()
}
if err != nil {
return nil, err
}
}
methods = append(methods, TraitMethodNode{
Name: name.Value, GenericParameters: genericParameters, Receiver: receiver,
Args: args, ReturnType: ret, Body: body, ExpressionBody: expressionBody,
Loc: p.SpanFrom(loc),
})
if p.Match(tokeniser.TokenComma, tokeniser.TokenSemicolon) {
p.Inc()
}
}
return &TraitTypeNode{Methods: methods, Loc: p.SpanFrom(begin)}, nil
}
func (p *Parser) ParseUnionType() (*UnionTypeNode, error) {
beginLoc := p.CurrLoc()
if !p.Match(tokeniser.TokenKeyword) || p.Peek().Value != string(tokeniser.KeywordUnion) {
return nil, shared.NewError(p.CurrLoc(), "expected 'union'")
}
p.Inc()
var tagType TypeNode
autoTag := false
if p.Match(tokeniser.TokenOpenParen) {
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenAt) {
p.Inc()
attribute, err := p.ParseIdent()
if err != nil {
return nil, err
}
if attribute.Name != "auto" {
return nil, shared.NewError(attribute.Loc, "unknown tagged union attribute @%s; expected @auto", attribute.Name)
}
autoTag = true
} else {
var err error
tagType, err = p.ParseType()
if err != nil {
return nil, err
}
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Expect(tokeniser.TokenCloseParen) {
return nil, shared.NewError(p.PrevLoc(), "expected ')' after tagged union tag type")
}
}
if !p.Expect(tokeniser.TokenOpenCurly) {
return nil, shared.NewError(p.PrevLoc(), "expected '{' after union")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
fields := []StructField{}
variants := []TaggedUnionVariantNode{}
seen := map[string]struct{}{}
for !p.Match(tokeniser.TokenCloseCurly) {
name, err := p.ParseIdent()
if err != nil {
return nil, err
}
if _, exists := seen[name.Name]; exists {
return nil, shared.NewError(name.Loc, "duplicate union field %q", name.Name)
}
seen[name.Name] = struct{}{}
if tagType != nil || autoTag {
variant := TaggedUnionVariantNode{Name: name.Name, Loc: name.Loc}
if p.Match(tokeniser.TokenOpenParen) {
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
fieldNames := map[string]struct{}{}
for !p.Match(tokeniser.TokenCloseParen) {
fieldName := ""
fieldLoc := p.CurrLoc()
if p.Match(tokeniser.TokenIdentifier) && p.Next().Type == tokeniser.TokenColon {
fieldName = p.Consume().Value
if _, duplicate := fieldNames[fieldName]; duplicate {
return nil, shared.NewError(fieldLoc, "duplicate tagged union payload field %q", fieldName)
}
fieldNames[fieldName] = struct{}{}
p.Inc()
}
fieldType, err := p.ParseType()
if err != nil {
return nil, err
}
variant.Fields = append(variant.Fields, StructField{Name: fieldName, Type: fieldType})
if p.Match(tokeniser.TokenCloseParen) {
break
}
if !p.Expect(tokeniser.TokenComma) {
return nil, shared.NewError(p.PrevLoc(), "expected ',' after tagged union payload field")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
}
if !p.Expect(tokeniser.TokenCloseParen) {
return nil, shared.NewError(p.PrevLoc(), "expected ')' after tagged union payload")
}
variant.Loc = name.Loc.WithEnd(p.PrevLoc())
}
variants = append(variants, variant)
if p.Match(tokeniser.TokenCloseCurly) {
break
}
if !p.Expect(tokeniser.TokenComma) {
return nil, shared.NewError(p.PrevLoc(), "expected ',' after tagged union variant")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
continue
}
if !p.Expect(tokeniser.TokenColon) {
return nil, shared.NewError(p.PrevLoc(), "expected ':' after union field")
}
fieldType, err := p.ParseType()
if err != nil {
return nil, err
}
fields = append(fields, StructField{Name: name.Name, Type: fieldType})
if p.Match(tokeniser.TokenCloseCurly) {
break
}
if !p.Expect(tokeniser.TokenComma) {
return nil, shared.NewError(p.PrevLoc(), "expected ',' after union field")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
}
if !p.Expect(tokeniser.TokenCloseCurly) {
return nil, shared.NewError(p.PrevLoc(), "expected '}' after union")
}
if tagType == nil && !autoTag && len(fields) == 0 {
return nil, shared.NewError(beginLoc, "union must declare at least one field")
}
if autoTag && len(variants) == 0 {
return nil, shared.NewError(beginLoc, "auto-tagged union must declare at least one variant")
}
return &UnionTypeNode{TagType: tagType, AutoTag: autoTag, Fields: fields, Variants: variants, Loc: p.SpanFrom(beginLoc)}, nil
}
func (p *Parser) ParseMatch(expression bool) (*MatchNode, error) {
begin := p.CurrLoc()
kw, ok := p.ExpectGet(tokeniser.TokenKeyword)
if !ok || kw.Value != string(tokeniser.KeywordMatch) {
return nil, shared.NewError(p.PrevLoc(), "expected 'match'")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
var subjects []ExpressionNode
for {
subject, err := p.ParseExpression()
if err != nil {
return nil, err
}
subjects = append(subjects, subject)
if !p.Match(tokeniser.TokenComma) {
break
}
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
}
node := &MatchNode{Subjects: subjects, Expression: expression}
var err error
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordAs) {
if len(subjects) != 1 {
return nil, shared.NewError(p.CurrLoc(), "a multi-subject match cannot use an 'as' binding")
}
p.Inc()
binding, ok := p.ExpectGet(tokeniser.TokenIdentifier)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected binding name after 'as'")
}
node.BindingName, node.BindingLoc = binding.Value, binding.Loc
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Expect(tokeniser.TokenOpenCurly) {
return nil, shared.NewError(p.PrevLoc(), "expected '{' after match subject")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
for !p.Match(tokeniser.TokenCloseCurly) {
armBegin := p.CurrLoc()
var patterns []*MatchPatternNode
for {
pattern, err := p.parseMatchPattern()
if err != nil {
return nil, err
}
patterns = append(patterns, pattern)
if !p.Match(tokeniser.TokenComma) {
break
}
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
}
var guard ExpressionNode
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordIf) {
p.Inc()
guard, err = p.ParseExpression()
if err != nil {
return nil, err
}
}
if !p.Expect(tokeniser.TokenFatArrow) {
return nil, shared.NewError(p.PrevLoc(), "expected '=>' after match pattern")
}
if len(patterns) != len(subjects) && !(len(patterns) == 1 && patterns[0].Kind == MatchPatternWildcard) {
return nil, shared.NewError(armBegin, "match arm has %d patterns for %d subjects", len(patterns), len(subjects))
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
bodyStartsWithBlock := p.Match(tokeniser.TokenOpenCurly)
var body ExpressionNode
if !expression && p.Match(tokeniser.TokenOpenCurly) {
body, err = p.ParseBlock()
} else {
body, err = p.ParseExpression()
}
if err != nil {
return nil, err
}
node.Arms = append(node.Arms, MatchArmNode{
Patterns: patterns, Guard: guard, Body: body, Loc: armBegin.WithEnd(body.GetLoc()),
})
if p.Match(tokeniser.TokenCloseCurly) {
break
}
_, blockBody := body.(*BlockNode)
blockBody = blockBody && bodyStartsWithBlock
if p.Match(tokeniser.TokenComma) {
p.Inc()
} else if !blockBody {
return nil, shared.NewError(p.PrevLoc(), "expected ',' after match arm")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
}
if len(node.Arms) == 0 {
return nil, shared.NewError(begin, "match must declare at least one arm")
}
if !p.Expect(tokeniser.TokenCloseCurly) {
return nil, shared.NewError(p.PrevLoc(), "expected '}' after match arms")
}
node.Loc = p.SpanFrom(begin)
return node, nil
}
func (p *Parser) parseMatchPattern() (*MatchPatternNode, error) {
begin := p.CurrLoc()
first, err := p.parseMatchPatternTerm()
if err != nil {
return nil, err
}
alternatives := []*MatchPatternNode{first}
for p.Match(tokeniser.TokenPipe) {
p.Inc()
alternative, err := p.parseMatchPatternTerm()
if err != nil {
return nil, err
}
alternatives = append(alternatives, alternative)
}
if len(alternatives) == 1 {
return first, nil
}
return &MatchPatternNode{Kind: MatchPatternAlternative, Alternatives: alternatives, Loc: begin.WithEnd(alternatives[len(alternatives)-1].Loc)}, nil
}
func (p *Parser) parseMatchPatternTerm() (*MatchPatternNode, error) {
begin := p.CurrLoc()
pattern, err := p.parseMatchPatternAtom()
if err != nil {
return nil, err
}
if !p.Match(tokeniser.Token2Dots) {
return pattern, nil
}
if pattern.Kind != MatchPatternLiteral {
return nil, shared.NewError(pattern.Loc, "range pattern must start with a literal")
}
p.Inc()
end, err := p.parseMatchPatternLiteral()
if err != nil {
return nil, shared.NewError(p.CurrLoc(), "expected literal after '..' in match range")
}
return &MatchPatternNode{Kind: MatchPatternRange, Start: pattern.Literal, End: end, Loc: begin.WithEnd(end.GetLoc())}, nil
}
func (p *Parser) parseMatchPatternAtom() (*MatchPatternNode, error) {
begin := p.CurrLoc()
if p.Match(tokeniser.TokenIdentifier) && p.Peek().Value == "_" {
p.Inc()
return &MatchPatternNode{Kind: MatchPatternWildcard, Loc: p.SpanFrom(begin)}, nil
}
if p.Match(tokeniser.TokenDot) {
p.Inc()
variant, ok := p.ExpectGet(tokeniser.TokenIdentifier)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected variant name after '.'")
}
pattern := &MatchPatternNode{Kind: MatchPatternVariant, Variant: variant.Value, Loc: p.SpanFrom(begin)}
if p.Match(tokeniser.TokenOpenParen) {
pattern.Payload = true
p.Inc()
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
for !p.Match(tokeniser.TokenCloseParen) {
binding, ok := p.ExpectGet(tokeniser.TokenIdentifier)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected payload binding in variant pattern")
}
entry := MatchBinding{Name: binding.Value, Loc: binding.Loc}
if p.Match(tokeniser.TokenEquals) {
p.Inc()
entry.Field = entry.Name
bound, ok := p.ExpectGet(tokeniser.TokenIdentifier)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected binding name after '=' in variant pattern")
}
entry.Name, entry.Loc = bound.Value, bound.Loc
}
pattern.Bindings = append(pattern.Bindings, entry)
if p.Match(tokeniser.TokenCloseParen) {
break
}
if !p.Expect(tokeniser.TokenComma) {
return nil, shared.NewError(p.PrevLoc(), "expected ',' after variant pattern binding")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
}
if !p.Expect(tokeniser.TokenCloseParen) {
return nil, shared.NewError(p.PrevLoc(), "expected ')' after variant pattern")
}
pattern.Loc = p.SpanFrom(begin)
}
return pattern, nil
}
literal, err := p.parseMatchPatternLiteral()
if err != nil {
return nil, err
}
return &MatchPatternNode{Kind: MatchPatternLiteral, Literal: literal, Loc: literal.GetLoc()}, nil
}
func (p *Parser) parseMatchPatternLiteral() (ExpressionNode, error) {
begin := p.CurrLoc()
switch {
case p.Match(tokeniser.TokenNumber):
tok := p.Consume()
if strings.Contains(tok.Value, ".") {
return &FloatLiteralNode{Value: tok.Value, Loc: p.SpanFrom(begin)}, nil
}
return &IntegerLiteralNode{Value: tok.Value, Loc: p.SpanFrom(begin)}, nil
case p.Match(tokeniser.TokenChar):
tok := p.Consume()
return &CharLiteralNode{Value: tok.Value[0], Loc: p.SpanFrom(begin)}, nil
case p.Match(tokeniser.TokenString):
tok := p.Consume()
return &StringLiteralNode{Value: tok.Value, Loc: p.SpanFrom(begin)}, nil
case p.Match(tokeniser.TokenCString):
tok := p.Consume()
return &CStringLiteralNode{Value: tok.Value, Loc: p.SpanFrom(begin)}, nil
case p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordTrue):
p.Inc()
return &BoolLiteralNode{Value: string(tokeniser.KeywordTrue), Loc: p.SpanFrom(begin)}, nil
case p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordFalse):
p.Inc()
return &BoolLiteralNode{Value: string(tokeniser.KeywordFalse), Loc: p.SpanFrom(begin)}, nil
default:
return nil, shared.NewError(begin, "expected match pattern")
}
}
func (p *Parser) ParseEnumType() (*EnumTypeNode, error) {
beginLoc := p.CurrLoc()
if !p.Match(tokeniser.TokenKeyword) || p.Peek().Value != string(tokeniser.KeywordEnum) {
return nil, shared.NewError(p.CurrLoc(), "expected 'enum'")
}
p.Inc()
if !p.Expect(tokeniser.TokenOpenCurly) {
return nil, shared.NewError(p.PrevLoc(), "expected '{' after enum")
}
variants := []string{}
values := []string{}
seen := map[string]struct{}{}
valueMode := -1 // 0 is implicit, 1 is explicit.
for {
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenCloseCurly) {
p.Inc()
break
}
variant, ok := p.ExpectGet(tokeniser.TokenIdentifier)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected enum variant")
}
if _, exists := seen[variant.Value]; exists {
return nil, shared.NewError(variant.Loc, "duplicate enum variant %q", variant.Value)
}
seen[variant.Value] = struct{}{}
variants = append(variants, variant.Value)
hasExplicitValue := p.Match(tokeniser.TokenEquals)
mode := 0
if hasExplicitValue {
mode = 1
}
if valueMode != -1 && valueMode != mode {
return nil, shared.NewError(variant.Loc, "cannot mix implicit and explicit enum values")
}
valueMode = mode
if hasExplicitValue {
p.Inc()
value, ok := p.ExpectGet(tokeniser.TokenNumber)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected integer literal after '=' in enum variant")
}
if !enumValueFits32Bits(value.Value) {
return nil, shared.NewError(value.Loc, "enum value %s does not fit in 32 bits", value.Value)
}
values = append(values, value.Value)
} else {
values = append(values, strconv.Itoa(len(variants)-1))
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenComma) {
p.Inc()
continue
}
if !p.Match(tokeniser.TokenCloseCurly) {
return nil, shared.NewError(p.CurrLoc(), "expected ',' or '}' after enum variant")
}
}
if len(variants) == 0 {
return nil, shared.NewError(beginLoc, "enum must declare at least one variant")
}
return &EnumTypeNode{Variants: variants, Values: values, Loc: p.SpanFrom(beginLoc)}, nil
}
func enumValueFits32Bits(value string) bool {
n, ok := new(big.Int).SetString(value, 10)
if !ok {
return false
}
min := big.NewInt(-1 << 31)
max := new(big.Int).SetUint64(1<<32 - 1)
return n.Cmp(min) >= 0 && n.Cmp(max) <= 0
}
func (p *Parser) ParseFlagsType() (*FlagsTypeNode, error) {
beginLoc := p.CurrLoc()
p.Inc()
if !p.Expect(tokeniser.TokenOpenParen) {
return nil, shared.NewError(p.PrevLoc(), "expected '(' after flags")
}
underlying, err := p.ParseNamedType()
if err != nil {
return nil, err
}
if !p.Expect(tokeniser.TokenCloseParen) || !p.Expect(tokeniser.TokenOpenCurly) {
return nil, shared.NewError(p.PrevLoc(), "expected '{' after flags underlying type")
}
known := map[string]*big.Int{}
var variants, values []string
valueMode := -1 // 0 is implicit, 1 is explicit.
for {
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenCloseCurly) {
p.Inc()
break
}
name, ok := p.ExpectGet(tokeniser.TokenIdentifier)
if !ok {
return nil, shared.NewError(p.PrevLoc(), "expected flag name")
}
if _, exists := known[name.Value]; exists {
return nil, shared.NewError(name.Loc, "duplicate flag %q", name.Value)
}
hasExplicitValue := p.Match(tokeniser.TokenEquals)
mode := 0
if hasExplicitValue {
mode = 1
}
if valueMode != -1 && valueMode != mode {
return nil, shared.NewError(name.Loc, "cannot mix implicit and explicit flag values")
}
valueMode = mode
var value *big.Int
if hasExplicitValue {
p.Inc()
var err error
value, err = p.parseFlagValue(known)
if err != nil {
return nil, err
}
} else {
value = new(big.Int).Lsh(big.NewInt(1), uint(len(variants)))
}
known[name.Value] = value
variants, values = append(variants, name.Value), append(values, value.String())
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenComma) {
p.Inc()
continue
}
if !p.Match(tokeniser.TokenCloseCurly) {
return nil, shared.NewError(p.CurrLoc(), "expected ',' or '}' after flag")
}
}
if len(variants) == 0 {
return nil, shared.NewError(beginLoc, "flags must declare at least one member")
}
return &FlagsTypeNode{Underlying: underlying, Variants: variants, Values: values, Loc: p.SpanFrom(beginLoc)}, nil
}
func (p *Parser) parseFlagValue(known map[string]*big.Int) (*big.Int, error) {
result := new(big.Int)
for {
var term *big.Int
if p.Match(tokeniser.TokenIdentifier) {
tok := p.Consume()
value, ok := known[tok.Value]
if !ok {
return nil, shared.NewError(tok.Loc, "unknown earlier flag %q", tok.Value)
}
term = new(big.Int).Set(value)
} else if p.Match(tokeniser.TokenNumber) {
tok := p.Consume()
if tok.NumberBase == 16 {
term, _ = new(big.Int).SetString(tok.Value, 10)
} else if tok.Value == "1" && p.Match(tokeniser.TokenShiftLeft) {
p.Inc()
shift, ok := p.ExpectGet(tokeniser.TokenNumber)
if !ok || shift.NumberBase != 10 {
return nil, shared.NewError(p.PrevLoc(), "expected decimal bit position after '1 <<'")
}
bit, ok := new(big.Int).SetString(shift.Value, 10)
if !ok || !bit.IsUint64() {
return nil, shared.NewError(shift.Loc, "invalid flag bit position")
}
term = new(big.Int).Lsh(big.NewInt(1), uint(bit.Uint64()))
} else {
return nil, shared.NewError(tok.Loc, "flag values must use hexadecimal, '1 << bit', or earlier flag names")
}
} else {
return nil, shared.NewError(p.CurrLoc(), "expected hexadecimal value, '1 << bit', or earlier flag name")
}
result.Or(result, term)
if !p.Match(tokeniser.TokenPipe) {
break
}
p.Inc()
}
return result, nil
}
func (p *Parser) ParseStructType() (*StructTypeNode, error) {
beginLoc := p.CurrLoc()
if !p.Match(tokeniser.TokenKeyword) || p.Peek().Value != string(tokeniser.KeywordStruct) {
return nil, shared.NewError(p.PrevLoc(), "expected 'struct' keyword")
}
p.Inc()
attrs, err := p.parseAttributes("")
if err != nil {
return nil, err
}
if !p.Expect(tokeniser.TokenOpenCurly) {
return nil, shared.NewError(p.PrevLoc(), "expected '{'")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
var fields []StructField
for !p.Match(tokeniser.TokenCloseCurly) {
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordUnion) {
fieldType, err := p.ParseUnionType()
if err != nil {
return nil, err
}
fields = append(fields, StructField{Type: fieldType})
if p.Match(tokeniser.TokenCloseCurly) {
break
}
if !p.Expect(tokeniser.TokenComma) {
return nil, shared.NewError(p.PrevLoc(), "expected ',' after struct field")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
continue
}
fieldName, err := p.ParseIdent()
if err != nil {
return nil, err
}
if !p.Expect(tokeniser.TokenColon) {
return nil, shared.NewError(p.PrevLoc(), "expected ':'")
}
fieldType, err := p.ParseType()
if err != nil {
return nil, err
}
fields = append(fields, StructField{
Name: fieldName.Name,
Type: fieldType,
})
if p.Match(tokeniser.TokenCloseCurly) {
break
}
if !p.Expect(tokeniser.TokenComma) {
return nil, shared.NewError(p.PrevLoc(), "expected ',' after struct field")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
}
if !p.Expect(tokeniser.TokenCloseCurly) {
return nil, shared.NewError(p.PrevLoc(), "expected '}'")
}
return &StructTypeNode{
Fields: fields,
Loc: p.SpanFrom(beginLoc),
Attributes: attrs,
}, nil
}
func (p *Parser) ParseArrayOrSliceType() (TypeNode, error) {
beginLoc := p.CurrLoc()
if !p.Expect(tokeniser.TokenOpenSquare) {
return nil, shared.NewError(p.PrevLoc(), "expected '[' to start slice type")
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenCloseSquare) {
p.Inc()
mutable := false
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordMut) {
mutable = true
p.Inc()
}
elementType, err := p.ParseType()
if err != nil {
return nil, err
}
return &SliceTypeNode{ElementType: elementType, Mutable: mutable, Loc: p.SpanFrom(beginLoc)}, nil
}
length, err := p.ParseExpression()
if err != nil {
return nil, err
}
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if !p.Expect(tokeniser.TokenCloseSquare) {
return nil, shared.NewError(p.PrevLoc(), "expected ']' after array length")
}
elementType, err := p.ParseType()
if err != nil {
return nil, err
}
return &ArrayTypeNode{ElementType: elementType, Length: length, Loc: p.SpanFrom(beginLoc)}, nil
}
func (p *Parser) ParsePointerType() (TypeNode, error) {
beginLoc := p.CurrLoc()
if !p.Expect(tokeniser.TokenAsterisk) {
return nil, shared.NewError(p.PrevLoc(), "expected '*' to start pointer type")
}
mutable := false
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordMut) {
p.Inc()
mutable = true
}
if p.Match(tokeniser.TokenKeyword) && p.Peek().Value == string(tokeniser.KeywordDyn) {
return p.ParseDynType(beginLoc, mutable)
}
var tpe TypeNode
var err error
if p.Match(tokeniser.TokenOpenParen) {
tpe, err = p.ParseFunctionType()
} else {
tpe, err = p.ParseType()
}
if err != nil {
return nil, err
}
if mutable {
if _, ok := tpe.(*FunctionTypeNode); ok {
return nil, shared.NewError(beginLoc, "function pointers cannot be mutable pointers")
}
}
return &PointerTypeNode{
BaseType: tpe,
Mutable: mutable,
Loc: p.SpanFrom(beginLoc),
}, nil
}
func (p *Parser) ParseFunctionType() (*FunctionTypeNode, error) {
beginLoc := p.CurrLoc()
p.Inc() // (
var params []TypeNode
typedVariadic := false
for {
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenCloseParen) {
break
}
isTypedVariadic := p.Match(tokeniser.Token3Dots)
if isTypedVariadic {
p.Inc()
}
param, err := p.ParseType()
if err != nil {
return nil, err
}
if isTypedVariadic {
param = &SliceTypeNode{ElementType: param, Loc: param.GetLoc()}
typedVariadic = true
}
params = append(params, param)
if isTypedVariadic {
if p.Match(tokeniser.TokenComma) {
return nil, shared.NewError(p.CurrLoc(), "typed variadic parameter must be last")
}
break
}
if !p.Match(tokeniser.TokenComma) {
break
}
p.Inc()
}
if !p.Expect(tokeniser.TokenCloseParen) {
return nil, shared.NewError(p.PrevLoc(), "expected ')' in function pointer type")
}
if !p.Expect(tokeniser.TokenColon) {
return nil, shared.NewError(p.PrevLoc(), "expected ':' and return type in function pointer type")
}
ret, err := p.ParseType()
if err != nil {
return nil, err
}
return &FunctionTypeNode{Parameters: params, ReturnType: ret, TypedVariadic: typedVariadic, Loc: p.SpanFrom(beginLoc)}, nil
}
func (p *Parser) ParseNamedType() (*NamedTypeNode, error) {
beginLoc := p.CurrLoc()
capture := false
if p.Match(tokeniser.TokenDollar) {
if !p.allowTypeCapture {
return nil, shared.NewError(p.CurrLoc(), "type captures are only valid in method owner patterns")
}
capture = true
p.Inc()
}
ident, err := p.ParseIdent()
if err != nil {
return nil, err
}
if !p.Match(tokeniser.TokenDot) {
node := &NamedTypeNode{
ModName: "",
Name: ident.Name,
Capture: capture,
Loc: p.SpanFrom(beginLoc),
}
if p.Match(tokeniser.TokenOpenParen) {
if capture {
return nil, shared.NewError(node.Loc, "captured type parameter %q cannot accept type arguments", node.Name)
}
arguments, err := p.parseCallTypeArguments()
if err != nil {
return nil, err
}
node.TypeArguments = arguments
node.Loc = p.SpanFrom(beginLoc)
}
if capture && p.Match(tokeniser.TokenColon) {
p.Inc()
constraint, err := p.ParseType()
if err != nil {
return nil, err
}
node.CaptureConstraint = constraint
node.Loc = p.SpanFrom(beginLoc)
}
return node, nil
}
if capture {
return nil, shared.NewError(beginLoc, "captured type parameter cannot be module-qualified")
}
parts := []string{ident.Name}
for p.Match(tokeniser.TokenDot) {
p.Inc()
realIdent, err := p.ParseIdent()
if err != nil {
return nil, err
}
parts = append(parts, realIdent.Name)
}
node := &NamedTypeNode{
ModName: strings.Join(parts[:len(parts)-1], "."),
Name: parts[len(parts)-1],
Loc: p.SpanFrom(beginLoc),
}
if p.Match(tokeniser.TokenOpenParen) {
arguments, err := p.parseCallTypeArguments()
if err != nil {
return nil, err
}
node.TypeArguments = arguments
node.Loc = p.SpanFrom(beginLoc)
}
return node, nil
}
func (p *Parser) parseCallTypeArguments() ([]TypeNode, error) {
if !p.Expect(tokeniser.TokenOpenParen) {
return nil, shared.NewError(p.PrevLoc(), "expected '('")
}
var arguments []TypeNode
for {
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenCloseParen) {
p.Inc()
return arguments, nil
}
argument, err := p.ParseType()
if err != nil {
return nil, err
}
arguments = append(arguments, argument)
for p.Match(tokeniser.TokenNewline) {
p.Inc()
}
if p.Match(tokeniser.TokenCloseParen) {
p.Inc()
return arguments, nil
}
if !p.Expect(tokeniser.TokenComma) {
return nil, shared.NewError(p.PrevLoc(), "expected ',' or ')' in type argument list")
}
}
}