Expand Gotlin language and tooling
This commit is contained in:
parent
8f4f858d86
commit
de1262b4cf
41 changed files with 6059 additions and 379 deletions
|
|
@ -2,6 +2,7 @@ package lang
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
|
@ -44,12 +45,92 @@ func (p *parser) parseProgram() (*Program, error) {
|
|||
return nil, err
|
||||
}
|
||||
prog.Interfaces = append(prog.Interfaces, decl)
|
||||
case p.check(tokenClass):
|
||||
case p.check(tokenEnum):
|
||||
decl, err := p.parseEnum()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prog.Enums = append(prog.Enums, decl)
|
||||
case p.check(tokenClass) || p.check(tokenData):
|
||||
decl, err := p.parseClass()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prog.Classes = append(prog.Classes, decl)
|
||||
case p.match(tokenAt):
|
||||
annotation, err := p.expect(tokenIdent, "expected annotation name")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if annotation.lexeme == "embed" {
|
||||
if _, err := p.expect(tokenLParen, "expected '(' after embed"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path, err := p.expect(tokenString, "expected embed path")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := p.expect(tokenRParen, "expected ')' after embed path"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := p.expect(tokenVal, "expected 'val' after embed annotation"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name, err := p.expect(tokenIdent, "expected embedded value name")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := p.expect(tokenColon, "expected ':' after embedded value name"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typ, err := p.parseTypeRef()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prog.Embeds = append(prog.Embeds, EmbedDecl{Path: strings.Trim(path.lexeme, "\""), Name: name.lexeme, Type: typ})
|
||||
continue
|
||||
}
|
||||
switch annotation.lexeme {
|
||||
case "jsonNaming":
|
||||
if _, err := p.expect(tokenLParen, "expected '(' after jsonNaming"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
policy, err := p.expect(tokenIdent, "expected JSON naming policy")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := p.expect(tokenRParen, "expected ')' after JSON naming policy"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !p.check(tokenData) {
|
||||
return nil, fmt.Errorf("jsonNaming is only valid on data classes")
|
||||
}
|
||||
decl, err := p.parseClass()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decl.JSONNaming = policy.lexeme
|
||||
prog.Classes = append(prog.Classes, decl)
|
||||
case "table":
|
||||
table, err := p.parseStringAnnotationArgument("table")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !validSQLName(table) {
|
||||
return nil, fmt.Errorf("invalid SQL table name %q", table)
|
||||
}
|
||||
if !p.check(tokenData) {
|
||||
return nil, fmt.Errorf("table is only valid on data classes")
|
||||
}
|
||||
decl, err := p.parseClass()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decl.Table = table
|
||||
prog.Classes = append(prog.Classes, decl)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported annotation %q", annotation.lexeme)
|
||||
}
|
||||
case p.check(tokenWorker):
|
||||
decl, err := p.parseWorker()
|
||||
if err != nil {
|
||||
|
|
@ -70,6 +151,64 @@ func (p *parser) parseProgram() (*Program, error) {
|
|||
return prog, nil
|
||||
}
|
||||
|
||||
func (p *parser) parseEnum() (EnumDecl, error) {
|
||||
if _, err := p.expect(tokenEnum, "expected 'enum'"); err != nil {
|
||||
return EnumDecl{}, err
|
||||
}
|
||||
name, err := p.expect(tokenIdent, "expected enum name")
|
||||
if err != nil {
|
||||
return EnumDecl{}, err
|
||||
}
|
||||
if _, err := p.expect(tokenLBrace, "expected '{' after enum name"); err != nil {
|
||||
return EnumDecl{}, err
|
||||
}
|
||||
var variants []EnumVariant
|
||||
seen := map[string]bool{}
|
||||
for !p.check(tokenRBrace) && !p.check(tokenEOF) {
|
||||
variant, err := p.expect(tokenIdent, "expected enum variant")
|
||||
if err != nil {
|
||||
return EnumDecl{}, err
|
||||
}
|
||||
if seen[variant.lexeme] {
|
||||
return EnumDecl{}, fmt.Errorf("duplicate enum variant %s", variant.lexeme)
|
||||
}
|
||||
seen[variant.lexeme] = true
|
||||
var payload []string
|
||||
if p.match(tokenLParen) {
|
||||
if !p.check(tokenRParen) {
|
||||
for {
|
||||
typ, err := p.parseTypeRef()
|
||||
if err != nil {
|
||||
return EnumDecl{}, err
|
||||
}
|
||||
payload = append(payload, typ)
|
||||
if !p.match(tokenComma) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, err := p.expect(tokenRParen, "expected ')' after enum payload"); err != nil {
|
||||
return EnumDecl{}, err
|
||||
}
|
||||
}
|
||||
stringValue := ""
|
||||
if p.match(tokenAssign) {
|
||||
value, err := p.expect(tokenString, "expected string enum value")
|
||||
if err != nil {
|
||||
return EnumDecl{}, err
|
||||
}
|
||||
stringValue = strings.Trim(value.lexeme, "\"")
|
||||
}
|
||||
variants = append(variants, EnumVariant{Name: variant.lexeme, PayloadTypes: payload, StringValue: stringValue})
|
||||
p.match(tokenComma)
|
||||
p.match(tokenSemicolon)
|
||||
}
|
||||
if _, err := p.expect(tokenRBrace, "expected '}' after enum"); err != nil {
|
||||
return EnumDecl{}, err
|
||||
}
|
||||
return EnumDecl{Name: name.lexeme, Variants: variants}, nil
|
||||
}
|
||||
|
||||
func (p *parser) parsePackageDecl() (string, error) {
|
||||
path, err := p.parseImportPath()
|
||||
if err != nil {
|
||||
|
|
@ -151,6 +290,7 @@ func (p *parser) parseInterface() (InterfaceDecl, error) {
|
|||
}
|
||||
|
||||
func (p *parser) parseClass() (ClassDecl, error) {
|
||||
data := p.match(tokenData)
|
||||
if _, err := p.expect(tokenClass, "expected 'class'"); err != nil {
|
||||
return ClassDecl{}, err
|
||||
}
|
||||
|
|
@ -174,7 +314,7 @@ func (p *parser) parseClass() (ClassDecl, error) {
|
|||
return ClassDecl{}, err
|
||||
}
|
||||
if !p.match(tokenLBrace) {
|
||||
return ClassDecl{Name: name.lexeme, Fields: fields, Parents: parents, Methods: nil}, nil
|
||||
return ClassDecl{Name: name.lexeme, Data: data, Fields: fields, Parents: parents, Methods: nil}, nil
|
||||
}
|
||||
|
||||
var methods []FunctionDecl
|
||||
|
|
@ -195,7 +335,7 @@ func (p *parser) parseClass() (ClassDecl, error) {
|
|||
return ClassDecl{}, err
|
||||
}
|
||||
|
||||
return ClassDecl{Name: name.lexeme, Fields: fields, Parents: parents, Methods: methods}, nil
|
||||
return ClassDecl{Name: name.lexeme, Data: data, Fields: fields, Parents: parents, Methods: methods}, nil
|
||||
}
|
||||
|
||||
func (p *parser) parseWorker() (WorkerDecl, error) {
|
||||
|
|
@ -284,6 +424,41 @@ func (p *parser) parseClassFields() ([]FieldDecl, error) {
|
|||
return fields, nil
|
||||
}
|
||||
for {
|
||||
column := ""
|
||||
id := false
|
||||
generated := false
|
||||
for p.match(tokenAt) {
|
||||
annotation, err := p.expect(tokenIdent, "expected field annotation name")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch annotation.lexeme {
|
||||
case "id":
|
||||
if id {
|
||||
return nil, fmt.Errorf("duplicate id annotation")
|
||||
}
|
||||
id = true
|
||||
case "generated":
|
||||
if generated {
|
||||
return nil, fmt.Errorf("duplicate generated annotation")
|
||||
}
|
||||
generated = true
|
||||
case "column":
|
||||
if column != "" {
|
||||
return nil, fmt.Errorf("duplicate column annotation")
|
||||
}
|
||||
column, err = p.parseStringAnnotationArgument("column")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !validSQLName(column) {
|
||||
return nil, fmt.Errorf("invalid SQL column name %q", column)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported field annotation %q", annotation.lexeme)
|
||||
}
|
||||
}
|
||||
private := p.match(tokenPrivate)
|
||||
mutable := false
|
||||
switch {
|
||||
case p.match(tokenVal):
|
||||
|
|
@ -305,13 +480,49 @@ func (p *parser) parseClassFields() ([]FieldDecl, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields = append(fields, FieldDecl{Mutable: mutable, Name: name.lexeme, Type: typ})
|
||||
fields = append(fields, FieldDecl{Mutable: mutable, Private: private, Name: name.lexeme, Type: typ, Column: column, ID: id, Generated: generated})
|
||||
if !p.match(tokenComma) {
|
||||
return fields, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) parseStringAnnotationArgument(name string) (string, error) {
|
||||
if _, err := p.expect(tokenLParen, "expected '(' after "+name); err != nil {
|
||||
return "", err
|
||||
}
|
||||
value, err := p.expect(tokenString, "expected string argument for "+name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := p.expect(tokenRParen, "expected ')' after "+name+" argument"); err != nil {
|
||||
return "", err
|
||||
}
|
||||
decoded, err := strconv.Unquote(value.lexeme)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid string argument for %s: %w", name, err)
|
||||
}
|
||||
return decoded, nil
|
||||
}
|
||||
|
||||
func validSQLName(name string) bool {
|
||||
if name == "" {
|
||||
return false
|
||||
}
|
||||
for i, r := range name {
|
||||
if i == 0 {
|
||||
if !isIdentStart(r) {
|
||||
return false
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !isIdentPart(r) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *parser) parseFunction() (FunctionDecl, error) {
|
||||
signature, err := p.parseFunctionSignature()
|
||||
if err != nil {
|
||||
|
|
@ -448,12 +659,25 @@ func (p *parser) parseStmt() (Stmt, error) {
|
|||
return nil, fmt.Errorf("'go' expects a function call expression")
|
||||
}
|
||||
return GoStmt{Value: expr}, nil
|
||||
case p.match(tokenDefer):
|
||||
expr, err := p.parseExpr(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := expr.(CallExpr); !ok {
|
||||
return nil, fmt.Errorf("'defer' expects a function call expression")
|
||||
}
|
||||
return DeferStmt{Value: expr}, nil
|
||||
case p.match(tokenIf):
|
||||
return p.parseIf()
|
||||
case p.match(tokenWhile):
|
||||
return p.parseWhile()
|
||||
case p.match(tokenFor):
|
||||
return p.parseForEach()
|
||||
case p.match(tokenSelect):
|
||||
return p.parseSelect()
|
||||
case p.match(tokenMatch):
|
||||
return p.parseMatch()
|
||||
case p.match(tokenTry):
|
||||
return p.parseTryCatch()
|
||||
case p.check(tokenIdent) && (p.peekN(1).kind == tokenAssign || p.peekN(1).kind == tokenComma || p.peekN(1).kind == tokenPlusAssign):
|
||||
|
|
@ -466,7 +690,7 @@ func (p *parser) parseStmt() (Stmt, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return AddAssignStmt{Name: name.lexeme, Value: value}, nil
|
||||
return AddAssignStmt{Name: name.lexeme, Pos: name.pos, Value: value}, nil
|
||||
}
|
||||
names, err := p.parseNameList()
|
||||
if err != nil {
|
||||
|
|
@ -480,9 +704,18 @@ func (p *parser) parseStmt() (Stmt, error) {
|
|||
return nil, err
|
||||
}
|
||||
if len(names) == 1 {
|
||||
return AssignStmt{Name: names[0], Value: value}, nil
|
||||
return AssignStmt{Name: names[0].lexeme, Pos: names[0].pos, Value: value}, nil
|
||||
}
|
||||
return MultiAssignStmt{Names: names, Value: value}, nil
|
||||
assign := MultiAssignStmt{
|
||||
Names: make([]string, 0, len(names)),
|
||||
Positions: make([]int, 0, len(names)),
|
||||
Value: value,
|
||||
}
|
||||
for _, name := range names {
|
||||
assign.Names = append(assign.Names, name.lexeme)
|
||||
assign.Positions = append(assign.Positions, name.pos)
|
||||
}
|
||||
return assign, nil
|
||||
default:
|
||||
expr, err := p.parseExpr(0)
|
||||
if err != nil {
|
||||
|
|
@ -492,6 +725,68 @@ func (p *parser) parseStmt() (Stmt, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *parser) parseMatch() (Stmt, error) {
|
||||
if _, err := p.expect(tokenLParen, "expected '(' after match"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value, err := p.parseExpr(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := p.expect(tokenRParen, "expected ')' after match value"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := p.expect(tokenLBrace, "expected '{' after match value"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cases []MatchCase
|
||||
for !p.check(tokenRBrace) && !p.check(tokenEOF) {
|
||||
enumName, err := p.expect(tokenIdent, "expected enum name in match case")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := p.expect(tokenDoubleColon, "expected '::' in match case"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
variant, err := p.expect(tokenIdent, "expected variant name")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var bindings []string
|
||||
if p.match(tokenLParen) {
|
||||
if !p.check(tokenRParen) {
|
||||
for {
|
||||
binding, err := p.expect(tokenIdent, "expected variant binding")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bindings = append(bindings, binding.lexeme)
|
||||
if !p.match(tokenComma) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, err := p.expect(tokenRParen, "expected ')' after variant bindings"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if _, err := p.expect(tokenArrow, "expected '->' after match pattern"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := p.parseBlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cases = append(cases, MatchCase{EnumName: enumName.lexeme, VariantName: variant.lexeme, Bindings: bindings, Body: body})
|
||||
p.match(tokenComma)
|
||||
p.match(tokenSemicolon)
|
||||
}
|
||||
if _, err := p.expect(tokenRBrace, "expected '}' after match"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return MatchStmt{Value: value, Cases: cases}, nil
|
||||
}
|
||||
|
||||
func (p *parser) parseSelect() (Stmt, error) {
|
||||
if _, err := p.expect(tokenLBrace, "expected '{' after select"); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -570,7 +865,7 @@ func (p *parser) parseVarDecl(mutable bool) (Stmt, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name := names[0]
|
||||
name := names[0].lexeme
|
||||
var typ string
|
||||
if p.match(tokenColon) {
|
||||
if len(names) > 1 {
|
||||
|
|
@ -592,7 +887,11 @@ func (p *parser) parseVarDecl(mutable bool) (Stmt, error) {
|
|||
if len(names) == 1 {
|
||||
return VarDecl{Mutable: mutable, Name: name, Type: typ, Value: value}, nil
|
||||
}
|
||||
return MultiVarDecl{Mutable: mutable, Names: names, Value: value}, nil
|
||||
decl := MultiVarDecl{Mutable: mutable, Names: make([]string, 0, len(names)), Value: value}
|
||||
for _, name := range names {
|
||||
decl.Names = append(decl.Names, name.lexeme)
|
||||
}
|
||||
return decl, nil
|
||||
}
|
||||
|
||||
func (p *parser) parseIf() (Stmt, error) {
|
||||
|
|
@ -662,9 +961,36 @@ func (p *parser) parsePrefix() (Expr, error) {
|
|||
tok := p.advance()
|
||||
switch tok.kind {
|
||||
case tokenIdent:
|
||||
if p.match(tokenDoubleColon) {
|
||||
variant, err := p.expect(tokenIdent, "expected enum variant")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var values []Expr
|
||||
if p.match(tokenLParen) {
|
||||
if !p.check(tokenRParen) {
|
||||
for {
|
||||
value, err := p.parseExpr(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values = append(values, value)
|
||||
if !p.match(tokenComma) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, err := p.expect(tokenRParen, "expected ')' after variant values"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return p.parsePostfix(EnumVariantExpr{EnumName: tok.lexeme, VariantName: variant.lexeme, Values: values})
|
||||
}
|
||||
return p.parsePostfix(IdentExpr{Name: tok.lexeme})
|
||||
case tokenInt:
|
||||
return IntExpr{Value: tok.lexeme}, nil
|
||||
case tokenFloat:
|
||||
return FloatExpr{Value: tok.lexeme}, nil
|
||||
case tokenString:
|
||||
return StringExpr{Value: tok.lexeme}, nil
|
||||
case tokenTrue:
|
||||
|
|
@ -684,7 +1010,7 @@ func (p *parser) parsePrefix() (Expr, error) {
|
|||
return p.parsePostfix(expr)
|
||||
case tokenLBrace:
|
||||
return p.parseLambdaExpr()
|
||||
case tokenBang, tokenMinus:
|
||||
case tokenBang, tokenMinus, tokenAmp, tokenStar:
|
||||
value, err := p.parseExpr(7)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -695,18 +1021,18 @@ func (p *parser) parsePrefix() (Expr, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *parser) parseNameList() ([]string, error) {
|
||||
func (p *parser) parseNameList() ([]token, error) {
|
||||
name, err := p.expect(tokenIdent, "expected variable name")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names := []string{name.lexeme}
|
||||
names := []token{name}
|
||||
for p.match(tokenComma) {
|
||||
next, err := p.expect(tokenIdent, "expected variable name")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names = append(names, next.lexeme)
|
||||
names = append(names, next)
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
|
@ -774,11 +1100,20 @@ func (p *parser) parsePostfix(expr Expr) (Expr, error) {
|
|||
for {
|
||||
switch {
|
||||
case p.match(tokenDot):
|
||||
name, err := p.expect(tokenIdent, "expected selector name")
|
||||
name := p.advance()
|
||||
if !selectorName(name.lexeme) {
|
||||
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(tokenLBracket):
|
||||
index, err := p.parseExpr(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
expr = SelectorExpr{Receiver: expr, Name: name.lexeme}
|
||||
if _, err := p.expect(tokenRBracket, "expected ']' after index"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
expr = IndexExpr{Receiver: expr, Index: index}
|
||||
case p.check(tokenLt):
|
||||
typeArgs, hasTypeArgs, err := p.tryParseCallTypeArgs()
|
||||
if err != nil {
|
||||
|
|
@ -790,44 +1125,31 @@ func (p *parser) parsePostfix(expr Expr) (Expr, error) {
|
|||
if _, err := p.expect(tokenLParen, "expected '(' after generic type arguments"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var args []Expr
|
||||
if !p.check(tokenRParen) {
|
||||
for {
|
||||
arg, err := p.parseExpr(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args = append(args, arg)
|
||||
if !p.match(tokenComma) {
|
||||
break
|
||||
}
|
||||
}
|
||||
args, namedArgs, err := p.parseCallArguments()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := p.expect(tokenRParen, "expected ')' after arguments"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
expr = CallExpr{Callee: expr, Args: args, TypeArgs: typeArgs}
|
||||
expr = CallExpr{Callee: expr, Args: args, NamedArgs: namedArgs, TypeArgs: typeArgs}
|
||||
case p.match(tokenLParen):
|
||||
var args []Expr
|
||||
if !p.check(tokenRParen) {
|
||||
for {
|
||||
arg, err := p.parseExpr(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args = append(args, arg)
|
||||
if !p.match(tokenComma) {
|
||||
break
|
||||
}
|
||||
}
|
||||
args, namedArgs, err := p.parseCallArguments()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := p.expect(tokenRParen, "expected ')' after arguments"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
expr = CallExpr{Callee: expr, Args: args}
|
||||
expr = CallExpr{Callee: expr, Args: args, NamedArgs: namedArgs}
|
||||
case p.check(tokenLBrace):
|
||||
call, ok := expr.(CallExpr)
|
||||
if !ok {
|
||||
var call CallExpr
|
||||
switch current := expr.(type) {
|
||||
case CallExpr:
|
||||
call = current
|
||||
case SelectorExpr:
|
||||
call = CallExpr{Callee: current}
|
||||
default:
|
||||
return expr, nil
|
||||
}
|
||||
lambda, err := p.parsePrefix()
|
||||
|
|
@ -842,6 +1164,54 @@ func (p *parser) parsePostfix(expr Expr) (Expr, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func selectorName(value string) bool {
|
||||
runes := []rune(value)
|
||||
if len(runes) == 0 || !isIdentStart(runes[0]) {
|
||||
return false
|
||||
}
|
||||
for _, r := range runes[1:] {
|
||||
if !isIdentPart(r) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *parser) parseCallArguments() ([]Expr, []NamedArg, error) {
|
||||
var args []Expr
|
||||
var named []NamedArg
|
||||
if p.check(tokenRParen) {
|
||||
return args, named, nil
|
||||
}
|
||||
for {
|
||||
if p.check(tokenIdent) && p.peekN(1).kind == tokenAssign {
|
||||
if len(args) > 0 {
|
||||
return nil, nil, fmt.Errorf("cannot mix positional and named arguments")
|
||||
}
|
||||
name := p.advance().lexeme
|
||||
p.advance()
|
||||
value, err := p.parseExpr(0)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
named = append(named, NamedArg{Name: name, Value: value})
|
||||
} else {
|
||||
if len(named) > 0 {
|
||||
return nil, nil, fmt.Errorf("cannot mix named and positional arguments")
|
||||
}
|
||||
value, err := p.parseExpr(0)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
args = append(args, value)
|
||||
}
|
||||
if !p.match(tokenComma) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return args, named, nil
|
||||
}
|
||||
|
||||
func (p *parser) tryParseCallTypeArgs() ([]string, bool, error) {
|
||||
if !p.check(tokenLt) {
|
||||
return nil, false, nil
|
||||
|
|
@ -937,6 +1307,9 @@ func (p *parser) parseTypeRef() (string, error) {
|
|||
}
|
||||
b += "<" + strings.Join(args, ", ") + ">"
|
||||
}
|
||||
if p.match(tokenQuestion) {
|
||||
b += "?"
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue