From 8a37dfeb49435f36a791fa92a6618afd7f3584f9 Mon Sep 17 00:00:00 2001 From: Ryo Nihei Date: Tue, 4 May 2021 00:44:47 +0900 Subject: Add lex mode lex mode is a feature that separates transition tables per each mode. The lexer starts from an initial state indicated by `initial_state` field and transitions between modes according to `push` and `pop` fields. The initial state will always be `default`. Currently, maleeni doesn't provide the ability to change the initial state. You can specify the modes of each lex entry using `modes` field. When the mode isn't indicated explicitly, the entries have `default` mode. --- compiler/compiler.go | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 2 deletions(-) (limited to 'compiler/compiler.go') diff --git a/compiler/compiler.go b/compiler/compiler.go index 15f42f3..6f878c5 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -42,12 +42,71 @@ func Compile(lexspec *spec.LexSpec, opts ...compilerOption) (*spec.CompiledLexSp } } + modeEntries, modes, modeNums := groupEntriesByLexMode(lexspec.Entries) + + modeSpecs := []*spec.CompiledLexModeSpec{ + nil, + } + for i, es := range modeEntries[1:] { + modeName := modes[i+1] + config.logger.Log("Compile %v mode:", modeName) + modeSpec, err := compile(es, modeNums, config) + if err != nil { + return nil, fmt.Errorf("failed to compile in %v mode: %w", modeName, err) + } + modeSpecs = append(modeSpecs, modeSpec) + } + + return &spec.CompiledLexSpec{ + InitialMode: spec.LexModeNumDefault, + Modes: modes, + Specs: modeSpecs, + }, nil +} + +func groupEntriesByLexMode(entries []*spec.LexEntry) ([][]*spec.LexEntry, []spec.LexModeName, map[spec.LexModeName]spec.LexModeNum) { + modes := []spec.LexModeName{ + spec.LexModeNameNil, + spec.LexModeNameDefault, + } + modeNums := map[spec.LexModeName]spec.LexModeNum{ + spec.LexModeNameNil: spec.LexModeNumNil, + spec.LexModeNameDefault: spec.LexModeNumDefault, + } + lastModeNum := spec.LexModeNumDefault + modeEntries := [][]*spec.LexEntry{ + nil, + []*spec.LexEntry{}, + } + for _, e := range entries { + ms := e.Modes + if len(ms) == 0 { + ms = []spec.LexModeName{ + spec.LexModeNameDefault, + } + } + for _, mode := range ms { + num, ok := modeNums[mode] + if !ok { + num = lastModeNum.Succ() + lastModeNum = num + modeNums[mode] = num + modes = append(modes, mode) + modeEntries = append(modeEntries, []*spec.LexEntry{}) + } + modeEntries[num] = append(modeEntries[num], e) + } + } + return modeEntries, modes, modeNums +} + +func compile(entries []*spec.LexEntry, modeNums map[spec.LexModeName]spec.LexModeNum, config *compilerConfig) (*spec.CompiledLexModeSpec, error) { var kinds []spec.LexKind var patterns map[int][]byte { kinds = append(kinds, spec.LexKindNil) patterns = map[int][]byte{} - for i, e := range lexspec.Entries { + for i, e := range entries { kinds = append(kinds, e.Kind) patterns[i+1] = []byte(e.Pattern) } @@ -58,6 +117,25 @@ func Compile(lexspec *spec.LexSpec, opts ...compilerOption) (*spec.CompiledLexSp } } + push := []spec.LexModeNum{ + spec.LexModeNumNil, + } + pop := []int{ + 0, + } + for _, e := range entries { + pushV := spec.LexModeNumNil + if e.Push != "" { + pushV = modeNums[e.Push] + } + push = append(push, pushV) + popV := 0 + if e.Pop { + popV = 1 + } + pop = append(pop, popV) + } + var root astNode var symTab *symbolTable { @@ -90,8 +168,10 @@ func Compile(lexspec *spec.LexSpec, opts ...compilerOption) (*spec.CompiledLexSp } } - return &spec.CompiledLexSpec{ + return &spec.CompiledLexModeSpec{ Kinds: kinds, + Push: push, + Pop: pop, DFA: tranTab, }, nil } -- cgit v1.2.3