s0rg/fantasyname

View on GitHub
parser.go

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
package fantasyname

import (
    "errors"
    "fmt"

    "github.com/s0rg/fantasyname/stringers"
    "github.com/s0rg/fantasyname/wrappers"
)

var (
    // ErrEmptyStack is returned, if parser counts stack as non-empty, but it was.
    ErrEmptyStack = errors.New("empty stack")
    // ErrInvalidSplit is returned when split - "|" is found in pattern out of any group.
    ErrInvalidSplit = errors.New("invalid split")
    // ErrUnbalancedGroup is returned when stuck upon unexpected group close/open brackets.
    ErrUnbalancedGroup = errors.New("unbalanced group")
)

type parser struct {
    conf  *config
    stack statestack
}

func newParser(opts ...Option) (p *parser) {
    c := &config{}

    for _, o := range opts {
        o(c)
    }

    c.validate()

    p = &parser{conf: c}

    // "root" state
    p.stack.Push(&state{
        rand: p.conf.RandIntN,
    })

    return p
}

func (p *parser) OnGroupStart(isLiteral bool) {
    p.stack.Push(&state{
        IsGroup:   true,
        IsLiteral: isLiteral,
        rand:      p.conf.RandIntN,
    })
}

func (p *parser) OnGroupEnd(isLiteral bool) (err error) {
    cur, ok := p.stack.Pop()
    if !ok {
        return ErrEmptyStack
    }

    top, ok := p.stack.Top()
    if !ok {
        return ErrEmptyStack
    }

    if !cur.IsGroup || cur.IsLiteral != isLiteral {
        return ErrUnbalancedGroup
    }

    top.Add(cur.Split().Stringer())

    return nil
}

func (p *parser) OnGroupSplit() (err error) {
    top, ok := p.stack.Top()
    if !ok {
        return ErrEmptyStack
    }

    if !top.IsGroup {
        return ErrInvalidSplit
    }

    top.Split()

    return nil
}

func (p *parser) Wrap(w wrapper) (err error) {
    top, ok := p.stack.Top()
    if !ok {
        return ErrEmptyStack
    }

    top.Wrap(w)

    return nil
}

func (p *parser) OnSymbol(r rune) (err error) {
    top, ok := p.stack.Top()
    if !ok {
        return ErrEmptyStack
    }

    var v fmt.Stringer

    if !top.IsLiteral {
        if sym, ok := p.conf.Dictionary[r]; ok {
            v = stringers.MakeRandom(sym, p.conf.RandIntN)
        }
    }

    if v == nil {
        v = stringers.Literal(string(r))
    }

    top.Add(v)

    return nil
}

func (p *parser) Build() (s fmt.Stringer, err error) {
    if len(p.stack) != 1 {
        return nil, ErrUnbalancedGroup
    }

    // no need to check `ok` - its already checked above.
    top, _ := p.stack.Top()

    s = top.Stringer()

    if p.conf.Collapse {
        s = wrappers.Collapsed(s)
    }

    return s, nil
}