1package chroma
  2
  3import (
  4	"encoding/xml"
  5	"fmt"
  6	"strings"
  7)
  8
  9// A Mutator modifies the behaviour of the lexer.
 10type Mutator interface {
 11	// Mutate the lexer state machine as it is processing.
 12	Mutate(state *LexerState) error
 13}
 14
 15// SerialisableMutator is a Mutator that can be serialised and deserialised.
 16type SerialisableMutator interface {
 17	Mutator
 18	MutatorKind() string
 19}
 20
 21// A LexerMutator is an additional interface that a Mutator can implement
 22// to modify the lexer when it is compiled.
 23type LexerMutator interface {
 24	// MutateLexer can be implemented to mutate the lexer itself.
 25	//
 26	// Rules are the lexer rules, state is the state key for the rule the mutator is associated with.
 27	MutateLexer(rules CompiledRules, state string, rule int) error
 28}
 29
 30// A MutatorFunc is a Mutator that mutates the lexer state machine as it is processing.
 31type MutatorFunc func(state *LexerState) error
 32
 33func (m MutatorFunc) Mutate(state *LexerState) error { return m(state) } // nolint
 34
 35type multiMutator struct {
 36	Mutators []Mutator `xml:"mutator"`
 37}
 38
 39func (m *multiMutator) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
 40	for {
 41		token, err := d.Token()
 42		if err != nil {
 43			return err
 44		}
 45		switch token := token.(type) {
 46		case xml.StartElement:
 47			mutator, err := unmarshalMutator(d, token)
 48			if err != nil {
 49				return err
 50			}
 51			m.Mutators = append(m.Mutators, mutator)
 52
 53		case xml.EndElement:
 54			return nil
 55		}
 56	}
 57}
 58
 59func (m *multiMutator) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
 60	name := xml.Name{Local: "mutators"}
 61	if err := e.EncodeToken(xml.StartElement{Name: name}); err != nil {
 62		return err
 63	}
 64	for _, m := range m.Mutators {
 65		if err := marshalMutator(e, m); err != nil {
 66			return err
 67		}
 68	}
 69	return e.EncodeToken(xml.EndElement{Name: name})
 70}
 71
 72func (m *multiMutator) MutatorKind() string { return "mutators" }
 73
 74func (m *multiMutator) Mutate(state *LexerState) error {
 75	for _, modifier := range m.Mutators {
 76		if err := modifier.Mutate(state); err != nil {
 77			return err
 78		}
 79	}
 80	return nil
 81}
 82
 83// Mutators applies a set of Mutators in order.
 84func Mutators(modifiers ...Mutator) Mutator {
 85	return &multiMutator{modifiers}
 86}
 87
 88type includeMutator struct {
 89	State string `xml:"state,attr"`
 90}
 91
 92// Include the given state.
 93func Include(state string) Rule {
 94	return Rule{Mutator: &includeMutator{state}}
 95}
 96
 97func (i *includeMutator) MutatorKind() string { return "include" }
 98
 99func (i *includeMutator) Mutate(s *LexerState) error {
100	return fmt.Errorf("should never reach here Include(%q)", i.State)
101}
102
103func (i *includeMutator) MutateLexer(rules CompiledRules, state string, rule int) error {
104	includedRules, ok := rules[i.State]
105	if !ok {
106		return fmt.Errorf("invalid include state %q", i.State)
107	}
108	rules[state] = append(rules[state][:rule], append(includedRules, rules[state][rule+1:]...)...)
109	return nil
110}
111
112type combinedMutator struct {
113	States []string `xml:"state,attr"`
114}
115
116func (c *combinedMutator) MutatorKind() string { return "combined" }
117
118// Combined creates a new anonymous state from the given states, and pushes that state.
119func Combined(states ...string) Mutator {
120	return &combinedMutator{states}
121}
122
123func (c *combinedMutator) Mutate(s *LexerState) error {
124	return fmt.Errorf("should never reach here Combined(%v)", c.States)
125}
126
127func (c *combinedMutator) MutateLexer(rules CompiledRules, state string, rule int) error {
128	name := "__combined_" + strings.Join(c.States, "__")
129	if _, ok := rules[name]; !ok {
130		combined := []*CompiledRule{}
131		for _, state := range c.States {
132			rules, ok := rules[state]
133			if !ok {
134				return fmt.Errorf("invalid combine state %q", state)
135			}
136			combined = append(combined, rules...)
137		}
138		rules[name] = combined
139	}
140	rules[state][rule].Mutator = Push(name)
141	return nil
142}
143
144type pushMutator struct {
145	States []string `xml:"state,attr"`
146}
147
148func (p *pushMutator) MutatorKind() string { return "push" }
149
150func (p *pushMutator) Mutate(s *LexerState) error {
151	if len(p.States) == 0 {
152		s.Stack = append(s.Stack, s.State)
153	} else {
154		for _, state := range p.States {
155			if state == "#pop" {
156				s.Stack = s.Stack[:len(s.Stack)-1]
157			} else {
158				s.Stack = append(s.Stack, state)
159			}
160		}
161	}
162	return nil
163}
164
165// Push states onto the stack.
166func Push(states ...string) Mutator {
167	return &pushMutator{states}
168}
169
170type popMutator struct {
171	Depth int `xml:"depth,attr"`
172}
173
174func (p *popMutator) MutatorKind() string { return "pop" }
175
176func (p *popMutator) Mutate(state *LexerState) error {
177	if len(state.Stack) == 0 {
178		return fmt.Errorf("nothing to pop")
179	}
180	state.Stack = state.Stack[:len(state.Stack)-p.Depth]
181	return nil
182}
183
184// Pop state from the stack when rule matches.
185func Pop(n int) Mutator {
186	return &popMutator{n}
187}
188
189// Default returns a Rule that applies a set of Mutators.
190func Default(mutators ...Mutator) Rule {
191	return Rule{Mutator: Mutators(mutators...)}
192}
193
194// Stringify returns the raw string for a set of tokens.
195func Stringify(tokens ...Token) string {
196	out := []string{}
197	for _, t := range tokens {
198		out = append(out, t.Value)
199	}
200	return strings.Join(out, "")
201}