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}