aboutsummaryrefslogtreecommitdiff
path: root/compiler/compiler.go
diff options
context:
space:
mode:
authorRyo Nihei <nihei.dev@gmail.com>2021-05-04 00:44:47 +0900
committerRyo Nihei <nihei.dev@gmail.com>2021-05-04 00:44:47 +0900
commit8a37dfeb49435f36a791fa92a6618afd7f3584f9 (patch)
tree99ba1262b0c01f7c8d2fa5fbfe92c7eff3314d82 /compiler/compiler.go
parentGenerate an invalid token from incompleted input. (diff)
downloadtre-8a37dfeb49435f36a791fa92a6618afd7f3584f9.tar.gz
tre-8a37dfeb49435f36a791fa92a6618afd7f3584f9.tar.xz
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.
Diffstat (limited to 'compiler/compiler.go')
-rw-r--r--compiler/compiler.go84
1 files changed, 82 insertions, 2 deletions
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
}