2024-07-28 00:16:36 -06:00
|
|
|
package parse
|
|
|
|
|
|
|
|
import "fmt"
|
|
|
|
|
|
|
|
// Parser is an embeddable type that contains parsing utilities.
|
|
|
|
type Parser struct {
|
|
|
|
// Lexer is the lexer that the parser will pull tokens from. This must
|
|
|
|
// be set before use.
|
|
|
|
Lexer Lexer
|
|
|
|
|
2024-07-28 00:29:49 -06:00
|
|
|
// TokenNames assigns names to token kinds. This should be set before
|
|
|
|
// use.
|
|
|
|
TokenNames map[TokenKind] string
|
|
|
|
|
2024-07-28 00:16:36 -06:00
|
|
|
// Token is the current token the parser is operating on.
|
|
|
|
Token Token
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 ...TokenKind) error {
|
|
|
|
if !this.Token.Is(allowed...) {
|
|
|
|
return Errorf (
|
2024-07-28 00:29:49 -06:00
|
|
|
this.Token.Position, "unexpected %s; expected %s",
|
|
|
|
this.tokenName(this.Token.Kind), commaList(this.tokenNames(allowed...)...))
|
2024-07-28 00:16:36 -06:00
|
|
|
}
|
|
|
|
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 ...TokenKind) error {
|
|
|
|
if !this.Token.Is(allowed...) {
|
|
|
|
return Errorf (
|
2024-07-28 00:29:49 -06:00
|
|
|
this.Token.Position, "unexpected %s; expected %s",
|
|
|
|
this.tokenName(this.Token.Kind), description)
|
2024-07-28 00:16:36 -06:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExpectNext is like Expect, but gets the next token first.
|
|
|
|
func (this *Parser) ExpectNext (allowed ...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 ...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 TokenKind, allowed ...string) error {
|
|
|
|
if !((this.Token.Is(kind) || kind == 0) && this.Token.ValueIs(allowed...)) {
|
|
|
|
return Errorf (
|
2024-07-28 00:29:49 -06:00
|
|
|
this.Token.Position, "unexpected %s; expected %s",
|
|
|
|
this.tokenName(this.Token.Kind), commaList(allowed))
|
2024-07-28 00:16:36 -06:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExpectValueDesc is like ExpectValue, but the expected value(s) are described
|
|
|
|
// manually.
|
|
|
|
func (this *Parser) ExpectValueDesc (description string, kind TokenKind, allowed ...string) error {
|
|
|
|
if !this.Token.Is(kind) || !this.Token.ValueIs(allowed...) {
|
|
|
|
return Errorf (
|
2024-07-28 00:29:49 -06:00
|
|
|
this.Token.Position, "unexpected %s; expected %s",
|
|
|
|
this.tokenName(this.Token.Kind), description)
|
2024-07-28 00:16:36 -06:00
|
|
|
}
|
|
|
|
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 () 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 ...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 () Position {
|
|
|
|
return this.Token.Position
|
|
|
|
}
|
|
|
|
|
|
|
|
// EOF returns whether or not the lexer has been exhausted.
|
|
|
|
func (this *Parser) EOF () bool {
|
|
|
|
return this.Token.EOF()
|
|
|
|
}
|
|
|
|
|
2024-07-28 00:29:49 -06:00
|
|
|
func (this *Parser) tokenName (kind TokenKind) string {
|
|
|
|
if this.TokenNames != nil {
|
|
|
|
if name, ok := this.TokenNames[kind]; ok {
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("TokenKind(%d)", kind)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *Parser) tokenNames (kinds ...TokenKind) []string {
|
|
|
|
names := make([]string, len(kinds))
|
|
|
|
for index, kind := range kinds {
|
|
|
|
names[index] = this.tokenName(kind)
|
|
|
|
}
|
|
|
|
return names
|
|
|
|
}
|
|
|
|
|
2024-07-28 00:16:36 -06:00
|
|
|
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
|
|
|
|
}
|