1056 lines
24 KiB
Go
1056 lines
24 KiB
Go
package lang
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
type parser struct {
|
|
tokens []token
|
|
pos int
|
|
}
|
|
|
|
func Parse(input string) (*Program, error) {
|
|
tokens, err := lex(input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p := &parser{tokens: tokens}
|
|
return p.parseProgram()
|
|
}
|
|
|
|
func (p *parser) parseProgram() (*Program, error) {
|
|
prog := &Program{}
|
|
for !p.check(tokenEOF) {
|
|
switch {
|
|
case p.match(tokenPackage):
|
|
if prog.PackagePath != "" {
|
|
return nil, fmt.Errorf("duplicate package declaration")
|
|
}
|
|
packagePath, err := p.parsePackageDecl()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
prog.PackagePath = packagePath
|
|
case p.match(tokenImport):
|
|
imp, err := p.parseImport()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
prog.Imports = append(prog.Imports, imp)
|
|
case p.check(tokenInterface):
|
|
decl, err := p.parseInterface()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
prog.Interfaces = append(prog.Interfaces, decl)
|
|
case p.check(tokenClass):
|
|
decl, err := p.parseClass()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
prog.Classes = append(prog.Classes, decl)
|
|
case p.check(tokenWorker):
|
|
decl, err := p.parseWorker()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
prog.Workers = append(prog.Workers, decl)
|
|
case p.check(tokenFun):
|
|
fn, err := p.parseFunction()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
prog.Functions = append(prog.Functions, fn)
|
|
default:
|
|
tok := p.peek()
|
|
return nil, fmt.Errorf("expected top-level declaration at %d, found %q", tok.pos, tok.lexeme)
|
|
}
|
|
}
|
|
return prog, nil
|
|
}
|
|
|
|
func (p *parser) parsePackageDecl() (string, error) {
|
|
path, err := p.parseImportPath()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
p.match(tokenSemicolon)
|
|
return path, nil
|
|
}
|
|
|
|
func (p *parser) parseImport() (ImportDecl, error) {
|
|
if p.check(tokenIdent) && p.peekN(1).kind == tokenString {
|
|
alias := p.advance().lexeme
|
|
path, err := p.expect(tokenString, "expected import path")
|
|
if err != nil {
|
|
return ImportDecl{}, err
|
|
}
|
|
p.match(tokenSemicolon)
|
|
return ImportDecl{Alias: alias, Path: path.lexeme}, nil
|
|
}
|
|
|
|
if p.check(tokenString) {
|
|
path, err := p.expect(tokenString, "expected import path")
|
|
if err != nil {
|
|
return ImportDecl{}, err
|
|
}
|
|
p.match(tokenSemicolon)
|
|
return ImportDecl{Path: path.lexeme}, nil
|
|
}
|
|
|
|
firstPath, err := p.parseImportPath()
|
|
if err != nil {
|
|
return ImportDecl{}, err
|
|
}
|
|
|
|
alias := ""
|
|
path := firstPath
|
|
if importPathContinues(p.peek()) {
|
|
alias = firstPath
|
|
path, err = p.parseImportPath()
|
|
if err != nil {
|
|
return ImportDecl{}, err
|
|
}
|
|
}
|
|
|
|
if err := validateBareImportPath(path); err != nil {
|
|
return ImportDecl{}, err
|
|
}
|
|
|
|
p.match(tokenSemicolon)
|
|
return ImportDecl{Alias: alias, Path: path}, nil
|
|
}
|
|
|
|
func (p *parser) parseInterface() (InterfaceDecl, error) {
|
|
if _, err := p.expect(tokenInterface, "expected 'interface'"); err != nil {
|
|
return InterfaceDecl{}, err
|
|
}
|
|
name, err := p.expect(tokenIdent, "expected interface name")
|
|
if err != nil {
|
|
return InterfaceDecl{}, err
|
|
}
|
|
if _, err := p.expect(tokenLBrace, "expected '{'"); err != nil {
|
|
return InterfaceDecl{}, err
|
|
}
|
|
|
|
var methods []FunctionSignature
|
|
for !p.check(tokenRBrace) && !p.check(tokenEOF) {
|
|
method, err := p.parseFunctionSignature()
|
|
if err != nil {
|
|
return InterfaceDecl{}, err
|
|
}
|
|
methods = append(methods, method)
|
|
p.match(tokenSemicolon)
|
|
}
|
|
if _, err := p.expect(tokenRBrace, "expected '}'"); err != nil {
|
|
return InterfaceDecl{}, err
|
|
}
|
|
|
|
return InterfaceDecl{Name: name.lexeme, Methods: methods}, nil
|
|
}
|
|
|
|
func (p *parser) parseClass() (ClassDecl, error) {
|
|
if _, err := p.expect(tokenClass, "expected 'class'"); err != nil {
|
|
return ClassDecl{}, err
|
|
}
|
|
name, err := p.expect(tokenIdent, "expected class name")
|
|
if err != nil {
|
|
return ClassDecl{}, err
|
|
}
|
|
var fields []FieldDecl
|
|
if p.match(tokenLParen) {
|
|
fields, err = p.parseClassFields()
|
|
if err != nil {
|
|
return ClassDecl{}, err
|
|
}
|
|
if _, err := p.expect(tokenRParen, "expected ')'"); err != nil {
|
|
return ClassDecl{}, err
|
|
}
|
|
}
|
|
|
|
parents, err := p.parseClassParents()
|
|
if err != nil {
|
|
return ClassDecl{}, err
|
|
}
|
|
if !p.match(tokenLBrace) {
|
|
return ClassDecl{Name: name.lexeme, Fields: fields, Parents: parents, Methods: nil}, nil
|
|
}
|
|
|
|
var methods []FunctionDecl
|
|
for !p.check(tokenRBrace) && !p.check(tokenEOF) {
|
|
p.match(tokenOverride)
|
|
if !p.check(tokenFun) {
|
|
tok := p.peek()
|
|
return ClassDecl{}, fmt.Errorf("expected class member at %d, found %q", tok.pos, tok.lexeme)
|
|
}
|
|
method, err := p.parseFunction()
|
|
if err != nil {
|
|
return ClassDecl{}, err
|
|
}
|
|
methods = append(methods, method)
|
|
p.match(tokenSemicolon)
|
|
}
|
|
if _, err := p.expect(tokenRBrace, "expected '}'"); err != nil {
|
|
return ClassDecl{}, err
|
|
}
|
|
|
|
return ClassDecl{Name: name.lexeme, Fields: fields, Parents: parents, Methods: methods}, nil
|
|
}
|
|
|
|
func (p *parser) parseWorker() (WorkerDecl, error) {
|
|
if _, err := p.expect(tokenWorker, "expected 'worker'"); err != nil {
|
|
return WorkerDecl{}, err
|
|
}
|
|
name, err := p.expect(tokenIdent, "expected worker name")
|
|
if err != nil {
|
|
return WorkerDecl{}, err
|
|
}
|
|
if _, err := p.expect(tokenLBrace, "expected '{'"); err != nil {
|
|
return WorkerDecl{}, err
|
|
}
|
|
|
|
var fields []WorkerFieldDecl
|
|
var methods []FunctionDecl
|
|
for !p.check(tokenRBrace) && !p.check(tokenEOF) {
|
|
switch {
|
|
case p.match(tokenVal), p.match(tokenVar):
|
|
mutable := p.tokens[p.pos-1].kind == tokenVar
|
|
fieldName, err := p.expect(tokenIdent, "expected field name")
|
|
if err != nil {
|
|
return WorkerDecl{}, err
|
|
}
|
|
typ := ""
|
|
if p.match(tokenColon) {
|
|
parsed, err := p.parseTypeRef()
|
|
if err != nil {
|
|
return WorkerDecl{}, err
|
|
}
|
|
typ = parsed
|
|
}
|
|
if _, err := p.expect(tokenAssign, "expected '=' after field declaration"); err != nil {
|
|
return WorkerDecl{}, err
|
|
}
|
|
value, err := p.parseExpr(0)
|
|
if err != nil {
|
|
return WorkerDecl{}, err
|
|
}
|
|
fields = append(fields, WorkerFieldDecl{
|
|
Mutable: mutable,
|
|
Name: fieldName.lexeme,
|
|
Type: typ,
|
|
Value: value,
|
|
})
|
|
p.match(tokenSemicolon)
|
|
case p.check(tokenFun):
|
|
method, err := p.parseFunction()
|
|
if err != nil {
|
|
return WorkerDecl{}, err
|
|
}
|
|
methods = append(methods, method)
|
|
p.match(tokenSemicolon)
|
|
default:
|
|
tok := p.peek()
|
|
return WorkerDecl{}, fmt.Errorf("expected worker member at %d, found %q", tok.pos, tok.lexeme)
|
|
}
|
|
}
|
|
if _, err := p.expect(tokenRBrace, "expected '}'"); err != nil {
|
|
return WorkerDecl{}, err
|
|
}
|
|
return WorkerDecl{Name: name.lexeme, Fields: fields, Methods: methods}, nil
|
|
}
|
|
|
|
func (p *parser) parseClassParents() ([]string, error) {
|
|
if !p.match(tokenColon) {
|
|
return nil, nil
|
|
}
|
|
|
|
var parents []string
|
|
for {
|
|
parent, err := p.parseTypeRef()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
parents = append(parents, parent)
|
|
if !p.match(tokenComma) {
|
|
return parents, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *parser) parseClassFields() ([]FieldDecl, error) {
|
|
var fields []FieldDecl
|
|
if p.check(tokenRParen) {
|
|
return fields, nil
|
|
}
|
|
for {
|
|
mutable := false
|
|
switch {
|
|
case p.match(tokenVal):
|
|
mutable = false
|
|
case p.match(tokenVar):
|
|
mutable = true
|
|
default:
|
|
tok := p.peek()
|
|
return nil, fmt.Errorf("expected 'val' or 'var' in class constructor at %d, found %q", tok.pos, tok.lexeme)
|
|
}
|
|
name, err := p.expect(tokenIdent, "expected field name")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := p.expect(tokenColon, "expected ':' after field name"); err != nil {
|
|
return nil, err
|
|
}
|
|
typ, err := p.parseTypeRef()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
fields = append(fields, FieldDecl{Mutable: mutable, Name: name.lexeme, Type: typ})
|
|
if !p.match(tokenComma) {
|
|
return fields, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *parser) parseFunction() (FunctionDecl, error) {
|
|
signature, err := p.parseFunctionSignature()
|
|
if err != nil {
|
|
return FunctionDecl{}, err
|
|
}
|
|
body, err := p.parseBlock()
|
|
if err != nil {
|
|
return FunctionDecl{}, err
|
|
}
|
|
|
|
return FunctionDecl{
|
|
Name: signature.Name,
|
|
Params: signature.Params,
|
|
ReturnType: signature.ReturnType,
|
|
Body: body,
|
|
}, nil
|
|
}
|
|
|
|
func (p *parser) parseFunctionSignature() (FunctionSignature, error) {
|
|
if _, err := p.expect(tokenFun, "expected 'fun'"); err != nil {
|
|
return FunctionSignature{}, err
|
|
}
|
|
name, err := p.expect(tokenIdent, "expected function name")
|
|
if err != nil {
|
|
return FunctionSignature{}, err
|
|
}
|
|
if _, err := p.expect(tokenLParen, "expected '('"); err != nil {
|
|
return FunctionSignature{}, err
|
|
}
|
|
params, err := p.parseParams()
|
|
if err != nil {
|
|
return FunctionSignature{}, err
|
|
}
|
|
if _, err := p.expect(tokenRParen, "expected ')'"); err != nil {
|
|
return FunctionSignature{}, err
|
|
}
|
|
|
|
returnType := "Unit"
|
|
if p.match(tokenColon) {
|
|
typ, err := p.parseTypeRef()
|
|
if err != nil {
|
|
return FunctionSignature{}, err
|
|
}
|
|
returnType = typ
|
|
}
|
|
|
|
return FunctionSignature{
|
|
Name: name.lexeme,
|
|
Params: params,
|
|
ReturnType: returnType,
|
|
}, nil
|
|
}
|
|
|
|
func (p *parser) parseParams() ([]Param, error) {
|
|
var params []Param
|
|
if p.check(tokenRParen) {
|
|
return params, nil
|
|
}
|
|
for {
|
|
name, err := p.expect(tokenIdent, "expected parameter name")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := p.expect(tokenColon, "expected ':' after parameter name"); err != nil {
|
|
return nil, err
|
|
}
|
|
typ, err := p.parseTypeRef()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params = append(params, Param{Name: name.lexeme, Type: typ})
|
|
if !p.match(tokenComma) {
|
|
return params, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *parser) parseBlock() ([]Stmt, error) {
|
|
if _, err := p.expect(tokenLBrace, "expected '{'"); err != nil {
|
|
return nil, err
|
|
}
|
|
var stmts []Stmt
|
|
for !p.check(tokenRBrace) && !p.check(tokenEOF) {
|
|
stmt, err := p.parseStmt()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
stmts = append(stmts, stmt)
|
|
p.match(tokenSemicolon)
|
|
}
|
|
if _, err := p.expect(tokenRBrace, "expected '}'"); err != nil {
|
|
return nil, err
|
|
}
|
|
return stmts, nil
|
|
}
|
|
|
|
func (p *parser) parseStmt() (Stmt, error) {
|
|
switch {
|
|
case p.match(tokenVal):
|
|
return p.parseVarDecl(false)
|
|
case p.match(tokenVar):
|
|
return p.parseVarDecl(true)
|
|
case p.match(tokenReturn):
|
|
if p.check(tokenSemicolon) || p.check(tokenRBrace) {
|
|
return ReturnStmt{}, nil
|
|
}
|
|
expr, err := p.parseExpr(0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ReturnStmt{Value: expr}, nil
|
|
case p.match(tokenThrow):
|
|
expr, err := p.parseExpr(0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ThrowStmt{Value: expr}, nil
|
|
case p.match(tokenGo):
|
|
var expr Expr
|
|
var err error
|
|
if p.check(tokenLBrace) {
|
|
body, err := p.parseBlock()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
expr = CallExpr{Callee: LambdaExpr{Body: body}}
|
|
} else {
|
|
expr, err = p.parseExpr(0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if _, ok := expr.(CallExpr); !ok {
|
|
return nil, fmt.Errorf("'go' expects a function call expression")
|
|
}
|
|
return GoStmt{Value: expr}, nil
|
|
case p.match(tokenIf):
|
|
return p.parseIf()
|
|
case p.match(tokenWhile):
|
|
return p.parseWhile()
|
|
case p.match(tokenSelect):
|
|
return p.parseSelect()
|
|
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):
|
|
if p.peekN(1).kind == tokenPlusAssign {
|
|
name := p.advance()
|
|
if _, err := p.expect(tokenPlusAssign, "expected '+=' in assignment"); err != nil {
|
|
return nil, err
|
|
}
|
|
value, err := p.parseExpr(0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return AddAssignStmt{Name: name.lexeme, Value: value}, nil
|
|
}
|
|
names, err := p.parseNameList()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := p.expect(tokenAssign, "expected '=' in assignment"); err != nil {
|
|
return nil, err
|
|
}
|
|
value, err := p.parseExpr(0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(names) == 1 {
|
|
return AssignStmt{Name: names[0], Value: value}, nil
|
|
}
|
|
return MultiAssignStmt{Names: names, Value: value}, nil
|
|
default:
|
|
expr, err := p.parseExpr(0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ExprStmt{Value: expr}, nil
|
|
}
|
|
}
|
|
|
|
func (p *parser) parseSelect() (Stmt, error) {
|
|
if _, err := p.expect(tokenLBrace, "expected '{' after select"); err != nil {
|
|
return nil, err
|
|
}
|
|
var cases []SelectCase
|
|
for !p.check(tokenRBrace) && !p.check(tokenEOF) {
|
|
source, err := p.parseExpr(0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := p.expect(tokenArrow, "expected '->' in select case"); err != nil {
|
|
return nil, err
|
|
}
|
|
var body []Stmt
|
|
if p.check(tokenLBrace) {
|
|
body, err = p.parseBlock()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
stmt, err := p.parseStmt()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
body = []Stmt{stmt}
|
|
p.match(tokenSemicolon)
|
|
}
|
|
cases = append(cases, SelectCase{Source: source, Body: body})
|
|
}
|
|
if _, err := p.expect(tokenRBrace, "expected '}' after select"); err != nil {
|
|
return nil, err
|
|
}
|
|
return SelectStmt{Cases: cases}, nil
|
|
}
|
|
|
|
func (p *parser) parseTryCatch() (Stmt, error) {
|
|
tryBody, err := p.parseBlock()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := p.expect(tokenCatch, "expected 'catch' after try block"); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := p.expect(tokenLParen, "expected '(' after catch"); err != nil {
|
|
return nil, err
|
|
}
|
|
name, err := p.expect(tokenIdent, "expected catch variable name")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
catchType := ""
|
|
if p.match(tokenColon) {
|
|
typ, err := p.parseTypeRef()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
catchType = typ
|
|
}
|
|
if _, err := p.expect(tokenRParen, "expected ')' after catch clause"); err != nil {
|
|
return nil, err
|
|
}
|
|
catchBody, err := p.parseBlock()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return TryCatchStmt{
|
|
TryBody: tryBody,
|
|
CatchName: name.lexeme,
|
|
CatchType: catchType,
|
|
CatchBody: catchBody,
|
|
}, nil
|
|
}
|
|
|
|
func (p *parser) parseVarDecl(mutable bool) (Stmt, error) {
|
|
names, err := p.parseNameList()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
name := names[0]
|
|
var typ string
|
|
if p.match(tokenColon) {
|
|
if len(names) > 1 {
|
|
return nil, fmt.Errorf("multi-variable declaration does not support explicit type annotations")
|
|
}
|
|
typeRef, err := p.parseTypeRef()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
typ = typeRef
|
|
}
|
|
if _, err := p.expect(tokenAssign, "expected '=' in declaration"); err != nil {
|
|
return nil, err
|
|
}
|
|
value, err := p.parseExpr(0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(names) == 1 {
|
|
return VarDecl{Mutable: mutable, Name: name, Type: typ, Value: value}, nil
|
|
}
|
|
return MultiVarDecl{Mutable: mutable, Names: names, Value: value}, nil
|
|
}
|
|
|
|
func (p *parser) parseIf() (Stmt, error) {
|
|
if _, err := p.expect(tokenLParen, "expected '(' after if"); err != nil {
|
|
return nil, err
|
|
}
|
|
cond, err := p.parseExpr(0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := p.expect(tokenRParen, "expected ')' after condition"); err != nil {
|
|
return nil, err
|
|
}
|
|
thenBlock, err := p.parseBlock()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var elseBlock []Stmt
|
|
if p.match(tokenElse) {
|
|
elseBlock, err = p.parseBlock()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return IfStmt{Cond: cond, Then: thenBlock, Else: elseBlock}, nil
|
|
}
|
|
|
|
func (p *parser) parseWhile() (Stmt, error) {
|
|
if _, err := p.expect(tokenLParen, "expected '(' after while"); err != nil {
|
|
return nil, err
|
|
}
|
|
cond, err := p.parseExpr(0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := p.expect(tokenRParen, "expected ')' after condition"); err != nil {
|
|
return nil, err
|
|
}
|
|
body, err := p.parseBlock()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return WhileStmt{Cond: cond, Body: body}, nil
|
|
}
|
|
|
|
func (p *parser) parseExpr(minPrec int) (Expr, error) {
|
|
left, err := p.parsePrefix()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for {
|
|
op := p.peek()
|
|
prec, ok := infixPrecedence(op.kind)
|
|
if !ok || prec < minPrec {
|
|
return left, nil
|
|
}
|
|
p.advance()
|
|
right, err := p.parseExpr(prec + 1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
left = BinaryExpr{Left: left, Op: op.lexeme, Right: right}
|
|
}
|
|
}
|
|
|
|
func (p *parser) parsePrefix() (Expr, error) {
|
|
tok := p.advance()
|
|
switch tok.kind {
|
|
case tokenIdent:
|
|
return p.parsePostfix(IdentExpr{Name: tok.lexeme})
|
|
case tokenInt:
|
|
return IntExpr{Value: tok.lexeme}, nil
|
|
case tokenString:
|
|
return StringExpr{Value: tok.lexeme}, nil
|
|
case tokenTrue:
|
|
return BoolExpr{Value: true}, nil
|
|
case tokenFalse:
|
|
return BoolExpr{Value: false}, nil
|
|
case tokenNull:
|
|
return NullExpr{}, nil
|
|
case tokenLParen:
|
|
expr, err := p.parseExpr(0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := p.expect(tokenRParen, "expected ')'"); err != nil {
|
|
return nil, err
|
|
}
|
|
return p.parsePostfix(expr)
|
|
case tokenLBrace:
|
|
return p.parseLambdaExpr()
|
|
case tokenBang, tokenMinus:
|
|
value, err := p.parseExpr(7)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return UnaryExpr{Op: tok.lexeme, Value: value}, nil
|
|
default:
|
|
return nil, fmt.Errorf("unexpected token %q at %d", tok.lexeme, tok.pos)
|
|
}
|
|
}
|
|
|
|
func (p *parser) parseNameList() ([]string, error) {
|
|
name, err := p.expect(tokenIdent, "expected variable name")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
names := []string{name.lexeme}
|
|
for p.match(tokenComma) {
|
|
next, err := p.expect(tokenIdent, "expected variable name")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
names = append(names, next.lexeme)
|
|
}
|
|
return names, nil
|
|
}
|
|
|
|
func (p *parser) parseLambdaExpr() (Expr, error) {
|
|
params, implicitIt, err := p.parseLambdaParams()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var body []Stmt
|
|
for !p.check(tokenRBrace) && !p.check(tokenEOF) {
|
|
stmt, err := p.parseStmt()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
body = append(body, stmt)
|
|
p.match(tokenSemicolon)
|
|
}
|
|
if _, err := p.expect(tokenRBrace, "expected '}' after lambda body"); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return LambdaExpr{Params: params, ImplicitIt: implicitIt, Body: body}, nil
|
|
}
|
|
|
|
func (p *parser) parseLambdaParams() ([]Param, bool, error) {
|
|
if p.match(tokenArrow) {
|
|
return nil, false, nil
|
|
}
|
|
|
|
saved := p.pos
|
|
var params []Param
|
|
for p.check(tokenIdent) {
|
|
name := p.advance()
|
|
param := Param{Name: name.lexeme}
|
|
if p.match(tokenColon) {
|
|
typ, err := p.parseTypeRef()
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
param.Type = typ
|
|
}
|
|
params = append(params, param)
|
|
if !p.match(tokenComma) {
|
|
break
|
|
}
|
|
}
|
|
|
|
if len(params) == 0 {
|
|
return nil, true, nil
|
|
}
|
|
if p.match(tokenArrow) {
|
|
return params, false, nil
|
|
}
|
|
|
|
if allUntypedLambdaParams(params) {
|
|
p.pos = saved
|
|
return nil, true, nil
|
|
}
|
|
return nil, false, fmt.Errorf("lambda parameters must either all specify types or all omit them")
|
|
}
|
|
|
|
func (p *parser) parsePostfix(expr Expr) (Expr, error) {
|
|
for {
|
|
switch {
|
|
case p.match(tokenDot):
|
|
name, err := p.expect(tokenIdent, "expected selector name")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
expr = SelectorExpr{Receiver: expr, Name: name.lexeme}
|
|
case p.check(tokenLt):
|
|
typeArgs, hasTypeArgs, err := p.tryParseCallTypeArgs()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !hasTypeArgs {
|
|
return expr, nil
|
|
}
|
|
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
|
|
}
|
|
}
|
|
}
|
|
if _, err := p.expect(tokenRParen, "expected ')' after arguments"); err != nil {
|
|
return nil, err
|
|
}
|
|
expr = CallExpr{Callee: expr, Args: args, 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
|
|
}
|
|
}
|
|
}
|
|
if _, err := p.expect(tokenRParen, "expected ')' after arguments"); err != nil {
|
|
return nil, err
|
|
}
|
|
expr = CallExpr{Callee: expr, Args: args}
|
|
case p.check(tokenLBrace):
|
|
call, ok := expr.(CallExpr)
|
|
if !ok {
|
|
return expr, nil
|
|
}
|
|
lambda, err := p.parsePrefix()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
call.Args = append(call.Args, lambda)
|
|
expr = call
|
|
default:
|
|
return expr, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *parser) tryParseCallTypeArgs() ([]string, bool, error) {
|
|
if !p.check(tokenLt) {
|
|
return nil, false, nil
|
|
}
|
|
|
|
saved := p.pos
|
|
p.advance()
|
|
|
|
var typeArgs []string
|
|
for {
|
|
arg, err := p.parseTypeRef()
|
|
if err != nil {
|
|
p.pos = saved
|
|
return nil, false, nil
|
|
}
|
|
typeArgs = append(typeArgs, arg)
|
|
if !p.match(tokenComma) {
|
|
break
|
|
}
|
|
}
|
|
if !p.match(tokenGt) {
|
|
p.pos = saved
|
|
return nil, false, nil
|
|
}
|
|
if !p.check(tokenLParen) {
|
|
p.pos = saved
|
|
return nil, false, nil
|
|
}
|
|
return typeArgs, true, nil
|
|
}
|
|
|
|
func (p *parser) parseTypeRef() (string, error) {
|
|
if p.match(tokenLParen) {
|
|
var params []string
|
|
if !p.check(tokenRParen) {
|
|
for {
|
|
paramType, err := p.parseTypeRef()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
params = append(params, paramType)
|
|
if !p.match(tokenComma) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if _, err := p.expect(tokenRParen, "expected ')' in function type"); err != nil {
|
|
return "", err
|
|
}
|
|
if _, err := p.expect(tokenArrow, "expected '->' in function type"); err != nil {
|
|
return "", err
|
|
}
|
|
ret, err := p.parseTypeRef()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return "(" + strings.Join(params, ", ") + ") -> " + ret, nil
|
|
}
|
|
|
|
var b string
|
|
for p.match(tokenStar) {
|
|
b += "*"
|
|
}
|
|
|
|
name, err := p.expect(tokenIdent, "expected type name")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
b += name.lexeme
|
|
|
|
for p.match(tokenDot) {
|
|
part, err := p.expect(tokenIdent, "expected selector name in type")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
b += "." + part.lexeme
|
|
}
|
|
|
|
if p.match(tokenLt) {
|
|
var args []string
|
|
for {
|
|
arg, err := p.parseTypeRef()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
args = append(args, arg)
|
|
if !p.match(tokenComma) {
|
|
break
|
|
}
|
|
}
|
|
if _, err := p.expect(tokenGt, "expected '>' in generic type"); err != nil {
|
|
return "", err
|
|
}
|
|
b += "<" + strings.Join(args, ", ") + ">"
|
|
}
|
|
|
|
return b, nil
|
|
}
|
|
|
|
func (p *parser) parseImportPath() (string, error) {
|
|
if !importPathStarts(p.peek()) {
|
|
tok := p.peek()
|
|
return "", fmt.Errorf("expected import path at %d, found %q", tok.pos, tok.lexeme)
|
|
}
|
|
|
|
var b strings.Builder
|
|
b.WriteString(p.advance().lexeme)
|
|
for {
|
|
if !(p.check(tokenDot) || p.check(tokenMinus)) {
|
|
break
|
|
}
|
|
sep := p.advance().lexeme
|
|
part, err := p.expect(tokenIdent, "expected import path segment")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
b.WriteString(sep)
|
|
b.WriteString(part.lexeme)
|
|
}
|
|
return b.String(), nil
|
|
}
|
|
|
|
func importPathStarts(tok token) bool {
|
|
return tok.kind == tokenIdent
|
|
}
|
|
|
|
func importPathContinues(tok token) bool {
|
|
switch tok.kind {
|
|
case tokenIdent:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func validateBareImportPath(path string) error {
|
|
if strings.HasPrefix(path, `"`) {
|
|
return nil
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func allUntypedLambdaParams(params []Param) bool {
|
|
if len(params) == 0 {
|
|
return false
|
|
}
|
|
for _, param := range params {
|
|
if param.Type != "" {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func infixPrecedence(kind tokenKind) (int, bool) {
|
|
switch kind {
|
|
case tokenOr:
|
|
return 1, true
|
|
case tokenAnd:
|
|
return 2, true
|
|
case tokenEq, tokenNeq:
|
|
return 3, true
|
|
case tokenLt, tokenLte, tokenGt, tokenGte:
|
|
return 4, true
|
|
case tokenPlus, tokenMinus:
|
|
return 5, true
|
|
case tokenStar, tokenSlash, tokenPercent:
|
|
return 6, true
|
|
default:
|
|
return 0, false
|
|
}
|
|
}
|
|
|
|
func (p *parser) expect(kind tokenKind, msg string) (token, error) {
|
|
if !p.check(kind) {
|
|
tok := p.peek()
|
|
return token{}, fmt.Errorf("%s at %d, found %q", msg, tok.pos, tok.lexeme)
|
|
}
|
|
return p.advance(), nil
|
|
}
|
|
|
|
func (p *parser) match(kind tokenKind) bool {
|
|
if p.check(kind) {
|
|
p.advance()
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (p *parser) check(kind tokenKind) bool {
|
|
return p.peek().kind == kind
|
|
}
|
|
|
|
func (p *parser) advance() token {
|
|
tok := p.tokens[p.pos]
|
|
if p.pos < len(p.tokens)-1 {
|
|
p.pos++
|
|
}
|
|
return tok
|
|
}
|
|
|
|
func (p *parser) peek() token {
|
|
return p.tokens[p.pos]
|
|
}
|
|
|
|
func (p *parser) peekN(offset int) token {
|
|
idx := p.pos + offset
|
|
if idx >= len(p.tokens) {
|
|
return p.tokens[len(p.tokens)-1]
|
|
}
|
|
return p.tokens[idx]
|
|
}
|