gotlin/internal/lang/parser.go
Pavel Flegr 8f4f858d86 init
2026-03-05 14:55:09 +01:00

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]
}