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}