mutators.go

  1package chroma
  2
  3import (
  4	"fmt"
  5	"strings"
  6)
  7
  8// A Mutator modifies the behaviour of the lexer.
  9type Mutator interface {
 10	// Mutate the lexer state machine as it is processing.
 11	Mutate(state *LexerState) error
 12}
 13
 14// A LexerMutator is an additional interface that a Mutator can implement
 15// to modify the lexer when it is compiled.
 16type LexerMutator interface {
 17	// Rules are the lexer rules, state is the state key for the rule the mutator is associated with.
 18	MutateLexer(rules CompiledRules, state string, rule int) error
 19}
 20
 21// A MutatorFunc is a Mutator that mutates the lexer state machine as it is processing.
 22type MutatorFunc func(state *LexerState) error
 23
 24func (m MutatorFunc) Mutate(state *LexerState) error { return m(state) } // nolint
 25
 26// Mutators applies a set of Mutators in order.
 27func Mutators(modifiers ...Mutator) MutatorFunc {
 28	return func(state *LexerState) error {
 29		for _, modifier := range modifiers {
 30			if err := modifier.Mutate(state); err != nil {
 31				return err
 32			}
 33		}
 34		return nil
 35	}
 36}
 37
 38type includeMutator struct {
 39	state string
 40}
 41
 42// Include the given state.
 43func Include(state string) Rule {
 44	return Rule{Mutator: &includeMutator{state}}
 45}
 46
 47func (i *includeMutator) Mutate(s *LexerState) error {
 48	return fmt.Errorf("should never reach here Include(%q)", i.state)
 49}
 50
 51func (i *includeMutator) MutateLexer(rules CompiledRules, state string, rule int) error {
 52	includedRules, ok := rules[i.state]
 53	if !ok {
 54		return fmt.Errorf("invalid include state %q", i.state)
 55	}
 56	rules[state] = append(rules[state][:rule], append(includedRules, rules[state][rule+1:]...)...)
 57	return nil
 58}
 59
 60type combinedMutator struct {
 61	states []string
 62}
 63
 64// Combined creates a new anonymous state from the given states, and pushes that state.
 65func Combined(states ...string) Mutator {
 66	return &combinedMutator{states}
 67}
 68
 69func (c *combinedMutator) Mutate(s *LexerState) error {
 70	return fmt.Errorf("should never reach here Combined(%v)", c.states)
 71}
 72
 73func (c *combinedMutator) MutateLexer(rules CompiledRules, state string, rule int) error {
 74	name := "__combined_" + strings.Join(c.states, "__")
 75	if _, ok := rules[name]; !ok {
 76		combined := []*CompiledRule{}
 77		for _, state := range c.states {
 78			rules, ok := rules[state]
 79			if !ok {
 80				return fmt.Errorf("invalid combine state %q", state)
 81			}
 82			combined = append(combined, rules...)
 83		}
 84		rules[name] = combined
 85	}
 86	rules[state][rule].Mutator = Push(name)
 87	return nil
 88}
 89
 90// Push states onto the stack.
 91func Push(states ...string) MutatorFunc {
 92	return func(s *LexerState) error {
 93		if len(states) == 0 {
 94			s.Stack = append(s.Stack, s.State)
 95		} else {
 96			for _, state := range states {
 97				if state == "#pop" {
 98					s.Stack = s.Stack[:len(s.Stack)-1]
 99				} else {
100					s.Stack = append(s.Stack, state)
101				}
102			}
103		}
104		return nil
105	}
106}
107
108// Pop state from the stack when rule matches.
109func Pop(n int) MutatorFunc {
110	return func(state *LexerState) error {
111		if len(state.Stack) == 0 {
112			return fmt.Errorf("nothing to pop")
113		}
114		state.Stack = state.Stack[:len(state.Stack)-n]
115		return nil
116	}
117}
118
119// Default returns a Rule that applies a set of Mutators.
120func Default(mutators ...Mutator) Rule {
121	return Rule{Mutator: Mutators(mutators...)}
122}
123
124// Stringify returns the raw string for a set of tokens.
125func Stringify(tokens ...Token) string {
126	out := []string{}
127	for _, t := range tokens {
128		out = append(out, t.Value)
129	}
130	return strings.Join(out, "")
131}