fspl/parser/parser.go

131 lines
3.8 KiB
Go

package parser
import "fmt"
import "git.tebibyte.media/fspl/fspl/lexer"
import "git.tebibyte.media/fspl/fspl/errors"
// Parser is an embeddable type that contains parsing utilities. It is used to
// build more specialized parsers.
type Parser struct {
Token lexer.Token
Lexer lexer.Lexer
}
// Expect checks the current token to see if it matches a list of token kind(s),
// else it returns an error describing what it expected.
func (this *Parser) Expect (allowed ...lexer.TokenKind) error {
if !this.Token.Is(allowed...) {
return errors.Errorf (
this.Token.Position, "unexpected %v; expected %s",
this.Token, commaList(allowed...))
}
return nil
}
// ExpectDesc is like Expect, but the expected entitie(s) are described
// manually. This can be helpful when a large syntactical entity is expected and
// the first token(s) of it would offer useless information to the user.
func (this *Parser) ExpectDesc (description string, allowed ...lexer.TokenKind) error {
if !this.Token.Is(allowed...) {
return errors.Errorf (
this.Token.Position, "unexpected %v; expected %s",
this.Token, description)
}
return nil
}
// ExpectNext is like Expect, but gets the next token first.
func (this *Parser) ExpectNext (allowed ...lexer.TokenKind) error {
err := this.Next()
if err != nil { return err }
return this.Expect(allowed...)
}
// ExpectNextDesc is like ExpectDesc, but gets the next token first.
func (this *Parser) ExpectNextDesc (description string, allowed ...lexer.TokenKind) error {
err := this.Next()
if err != nil { return err }
return this.ExpectDesc(description, allowed...)
}
// ExpectValue returns an error if the current token's value does not match the
// allowed values.
func (this *Parser) ExpectValue (kind lexer.TokenKind, allowed ...string) error {
if !((this.Token.Is(kind) || kind == 0) && this.Token.ValueIs(allowed...)) {
return errors.Errorf (
this.Token.Position, "unexpected %v; expected %s",
this.Token, commaList(allowed))
}
return nil
}
// ExpectValueDesc is like ExpectValue, but the expected value(s) are described
// manually.
func (this *Parser) ExpectValueDesc (description string, kind lexer.TokenKind, allowed ...string) error {
if !this.Token.Is(kind) || !this.Token.ValueIs(allowed...) {
return errors.Errorf (
this.Token.Position, "unexpected %v; expected %s",
this.Token, description)
}
return nil
}
// Next consumes a token from the lexer, and sets it as the current token.
func (this *Parser) Next () error {
token, err := this.Lexer.Next()
if err != nil { return err }
this.Token = token
return nil
}
// Kind returns the token kind of the current token.
func (this *Parser) Kind () lexer.TokenKind {
return this.Token.Kind
}
// Value returns the value of the current token.
func (this *Parser) Value () string {
return this.Token.Value
}
// Is returns whether or not the current token matches any of the given kinds.
func (this *Parser) Is (allowed ...lexer.TokenKind) bool {
return this.Token.Is(allowed...)
}
// ValueIs returns whether or not the current token matches any of the given
// values.
func (this *Parser) ValueIs (allowed ...string) bool {
return this.Token.ValueIs(allowed...)
}
// Pos returns the position of the current token.
func (this *Parser) Pos () errors.Position {
return this.Token.Position
}
// EOF returns whether or not the lexer has been exhausted.
func (this *Parser) EOF () bool {
return this.Token.EOF()
}
func commaList[ELEMENT any] (items ...ELEMENT) string {
list := ""
switch {
case len(items) == 1: list = fmt.Sprint(items[0])
case len(items) == 2: list = fmt.Sprint(items[0], " or ", items[1])
default:
for index, item := range items {
if index > 0 {
list += ", "
if index == len(items) - 1 {
list += " or "
}
}
list += fmt.Sprintf("%v", item)
}
}
return list
}