Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
131 lines
3.3 KiB
131 lines
3.3 KiB
package chroma |
|
|
|
import ( |
|
"fmt" |
|
"strings" |
|
) |
|
|
|
// A Mutator modifies the behaviour of the lexer. |
|
type Mutator interface { |
|
// Mutate the lexer state machine as it is processing. |
|
Mutate(state *LexerState) error |
|
} |
|
|
|
// A LexerMutator is an additional interface that a Mutator can implement |
|
// to modify the lexer when it is compiled. |
|
type LexerMutator interface { |
|
// Rules are the lexer rules, state is the state key for the rule the mutator is associated with. |
|
MutateLexer(rules CompiledRules, state string, rule int) error |
|
} |
|
|
|
// A MutatorFunc is a Mutator that mutates the lexer state machine as it is processing. |
|
type MutatorFunc func(state *LexerState) error |
|
|
|
func (m MutatorFunc) Mutate(state *LexerState) error { return m(state) } // nolint |
|
|
|
// Mutators applies a set of Mutators in order. |
|
func Mutators(modifiers ...Mutator) MutatorFunc { |
|
return func(state *LexerState) error { |
|
for _, modifier := range modifiers { |
|
if err := modifier.Mutate(state); err != nil { |
|
return err |
|
} |
|
} |
|
return nil |
|
} |
|
} |
|
|
|
type includeMutator struct { |
|
state string |
|
} |
|
|
|
// Include the given state. |
|
func Include(state string) Rule { |
|
return Rule{Mutator: &includeMutator{state}} |
|
} |
|
|
|
func (i *includeMutator) Mutate(s *LexerState) error { |
|
return fmt.Errorf("should never reach here Include(%q)", i.state) |
|
} |
|
|
|
func (i *includeMutator) MutateLexer(rules CompiledRules, state string, rule int) error { |
|
includedRules, ok := rules[i.state] |
|
if !ok { |
|
return fmt.Errorf("invalid include state %q", i.state) |
|
} |
|
rules[state] = append(rules[state][:rule], append(includedRules, rules[state][rule+1:]...)...) |
|
return nil |
|
} |
|
|
|
type combinedMutator struct { |
|
states []string |
|
} |
|
|
|
// Combined creates a new anonymous state from the given states, and pushes that state. |
|
func Combined(states ...string) Mutator { |
|
return &combinedMutator{states} |
|
} |
|
|
|
func (c *combinedMutator) Mutate(s *LexerState) error { |
|
return fmt.Errorf("should never reach here Combined(%v)", c.states) |
|
} |
|
|
|
func (c *combinedMutator) MutateLexer(rules CompiledRules, state string, rule int) error { |
|
name := "__combined_" + strings.Join(c.states, "__") |
|
if _, ok := rules[name]; !ok { |
|
combined := []*CompiledRule{} |
|
for _, state := range c.states { |
|
rules, ok := rules[state] |
|
if !ok { |
|
return fmt.Errorf("invalid combine state %q", state) |
|
} |
|
combined = append(combined, rules...) |
|
} |
|
rules[name] = combined |
|
} |
|
rules[state][rule].Mutator = Push(name) |
|
return nil |
|
} |
|
|
|
// Push states onto the stack. |
|
func Push(states ...string) MutatorFunc { |
|
return func(s *LexerState) error { |
|
if len(states) == 0 { |
|
s.Stack = append(s.Stack, s.State) |
|
} else { |
|
for _, state := range states { |
|
if state == "#pop" { |
|
s.Stack = s.Stack[:len(s.Stack)-1] |
|
} else { |
|
s.Stack = append(s.Stack, state) |
|
} |
|
} |
|
} |
|
return nil |
|
} |
|
} |
|
|
|
// Pop state from the stack when rule matches. |
|
func Pop(n int) MutatorFunc { |
|
return func(state *LexerState) error { |
|
if len(state.Stack) == 0 { |
|
return fmt.Errorf("nothing to pop") |
|
} |
|
state.Stack = state.Stack[:len(state.Stack)-n] |
|
return nil |
|
} |
|
} |
|
|
|
// Default returns a Rule that applies a set of Mutators. |
|
func Default(mutators ...Mutator) Rule { |
|
return Rule{Mutator: Mutators(mutators...)} |
|
} |
|
|
|
// Stringify returns the raw string for a set of tokens. |
|
func Stringify(tokens ...Token) string { |
|
out := []string{} |
|
for _, t := range tokens { |
|
out = append(out, t.Value) |
|
} |
|
return strings.Join(out, "") |
|
}
|
|
|