diff vendor/github.com/eknkc/amber/parser/scanner.go @ 66:787b5ee0289d draft

Use vendored modules Signed-off-by: Izuru Yakumo <yakumo.izuru@chaotic.ninja>
author yakumo.izuru
date Sun, 23 Jul 2023 13:18:53 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/github.com/eknkc/amber/parser/scanner.go	Sun Jul 23 13:18:53 2023 +0000
@@ -0,0 +1,501 @@
+package parser
+
+import (
+	"bufio"
+	"container/list"
+	"fmt"
+	"io"
+	"regexp"
+)
+
+const (
+	tokEOF = -(iota + 1)
+	tokDoctype
+	tokComment
+	tokIndent
+	tokOutdent
+	tokBlank
+	tokId
+	tokClassName
+	tokTag
+	tokText
+	tokAttribute
+	tokIf
+	tokElse
+	tokEach
+	tokAssignment
+	tokImport
+	tokNamedBlock
+	tokExtends
+	tokMixin
+	tokMixinCall
+)
+
+const (
+	scnNewLine = iota
+	scnLine
+	scnEOF
+)
+
+type scanner struct {
+	reader      *bufio.Reader
+	indentStack *list.List
+	stash       *list.List
+
+	state  int32
+	buffer string
+
+	line          int
+	col           int
+	lastTokenLine int
+	lastTokenCol  int
+	lastTokenSize int
+
+	readRaw bool
+}
+
+type token struct {
+	Kind  rune
+	Value string
+	Data  map[string]string
+}
+
+func newScanner(r io.Reader) *scanner {
+	s := new(scanner)
+	s.reader = bufio.NewReader(r)
+	s.indentStack = list.New()
+	s.stash = list.New()
+	s.state = scnNewLine
+	s.line = -1
+	s.col = 0
+
+	return s
+}
+
+func (s *scanner) Pos() SourcePosition {
+	return SourcePosition{s.lastTokenLine + 1, s.lastTokenCol + 1, s.lastTokenSize, ""}
+}
+
+// Returns next token found in buffer
+func (s *scanner) Next() *token {
+	if s.readRaw {
+		s.readRaw = false
+		return s.NextRaw()
+	}
+
+	s.ensureBuffer()
+
+	if stashed := s.stash.Front(); stashed != nil {
+		tok := stashed.Value.(*token)
+		s.stash.Remove(stashed)
+		return tok
+	}
+
+	switch s.state {
+	case scnEOF:
+		if outdent := s.indentStack.Back(); outdent != nil {
+			s.indentStack.Remove(outdent)
+			return &token{tokOutdent, "", nil}
+		}
+
+		return &token{tokEOF, "", nil}
+	case scnNewLine:
+		s.state = scnLine
+
+		if tok := s.scanIndent(); tok != nil {
+			return tok
+		}
+
+		return s.Next()
+	case scnLine:
+		if tok := s.scanMixin(); tok != nil {
+			return tok
+		}
+
+		if tok := s.scanMixinCall(); tok != nil {
+			return tok
+		}
+
+		if tok := s.scanDoctype(); tok != nil {
+			return tok
+		}
+
+		if tok := s.scanCondition(); tok != nil {
+			return tok
+		}
+
+		if tok := s.scanEach(); tok != nil {
+			return tok
+		}
+
+		if tok := s.scanImport(); tok != nil {
+			return tok
+		}
+
+		if tok := s.scanExtends(); tok != nil {
+			return tok
+		}
+
+		if tok := s.scanBlock(); tok != nil {
+			return tok
+		}
+
+		if tok := s.scanAssignment(); tok != nil {
+			return tok
+		}
+
+		if tok := s.scanTag(); tok != nil {
+			return tok
+		}
+
+		if tok := s.scanId(); tok != nil {
+			return tok
+		}
+
+		if tok := s.scanClassName(); tok != nil {
+			return tok
+		}
+
+		if tok := s.scanAttribute(); tok != nil {
+			return tok
+		}
+
+		if tok := s.scanComment(); tok != nil {
+			return tok
+		}
+
+		if tok := s.scanText(); tok != nil {
+			return tok
+		}
+	}
+
+	return nil
+}
+
+func (s *scanner) NextRaw() *token {
+	result := ""
+	level := 0
+
+	for {
+		s.ensureBuffer()
+
+		switch s.state {
+		case scnEOF:
+			return &token{tokText, result, map[string]string{"Mode": "raw"}}
+		case scnNewLine:
+			s.state = scnLine
+
+			if tok := s.scanIndent(); tok != nil {
+				if tok.Kind == tokIndent {
+					level++
+				} else if tok.Kind == tokOutdent {
+					level--
+				} else {
+					result = result + "\n"
+					continue
+				}
+
+				if level < 0 {
+					s.stash.PushBack(&token{tokOutdent, "", nil})
+
+					if len(result) > 0 && result[len(result)-1] == '\n' {
+						result = result[:len(result)-1]
+					}
+
+					return &token{tokText, result, map[string]string{"Mode": "raw"}}
+				}
+			}
+		case scnLine:
+			if len(result) > 0 {
+				result = result + "\n"
+			}
+			for i := 0; i < level; i++ {
+				result += "\t"
+			}
+			result = result + s.buffer
+			s.consume(len(s.buffer))
+		}
+	}
+
+	return nil
+}
+
+var rgxIndent = regexp.MustCompile(`^(\s+)`)
+
+func (s *scanner) scanIndent() *token {
+	if len(s.buffer) == 0 {
+		return &token{tokBlank, "", nil}
+	}
+
+	var head *list.Element
+	for head = s.indentStack.Front(); head != nil; head = head.Next() {
+		value := head.Value.(*regexp.Regexp)
+
+		if match := value.FindString(s.buffer); len(match) != 0 {
+			s.consume(len(match))
+		} else {
+			break
+		}
+	}
+
+	newIndent := rgxIndent.FindString(s.buffer)
+
+	if len(newIndent) != 0 && head == nil {
+		s.indentStack.PushBack(regexp.MustCompile(regexp.QuoteMeta(newIndent)))
+		s.consume(len(newIndent))
+		return &token{tokIndent, newIndent, nil}
+	}
+
+	if len(newIndent) == 0 && head != nil {
+		for head != nil {
+			next := head.Next()
+			s.indentStack.Remove(head)
+			if next == nil {
+				return &token{tokOutdent, "", nil}
+			} else {
+				s.stash.PushBack(&token{tokOutdent, "", nil})
+			}
+			head = next
+		}
+	}
+
+	if len(newIndent) != 0 && head != nil {
+		panic("Mismatching indentation. Please use a coherent indent schema.")
+	}
+
+	return nil
+}
+
+var rgxDoctype = regexp.MustCompile(`^(!!!|doctype)\s*(.*)`)
+
+func (s *scanner) scanDoctype() *token {
+	if sm := rgxDoctype.FindStringSubmatch(s.buffer); len(sm) != 0 {
+		if len(sm[2]) == 0 {
+			sm[2] = "html"
+		}
+
+		s.consume(len(sm[0]))
+		return &token{tokDoctype, sm[2], nil}
+	}
+
+	return nil
+}
+
+var rgxIf = regexp.MustCompile(`^if\s+(.+)$`)
+var rgxElse = regexp.MustCompile(`^else\s*`)
+
+func (s *scanner) scanCondition() *token {
+	if sm := rgxIf.FindStringSubmatch(s.buffer); len(sm) != 0 {
+		s.consume(len(sm[0]))
+		return &token{tokIf, sm[1], nil}
+	}
+
+	if sm := rgxElse.FindStringSubmatch(s.buffer); len(sm) != 0 {
+		s.consume(len(sm[0]))
+		return &token{tokElse, "", nil}
+	}
+
+	return nil
+}
+
+var rgxEach = regexp.MustCompile(`^each\s+(\$[\w0-9\-_]*)(?:\s*,\s*(\$[\w0-9\-_]*))?\s+in\s+(.+)$`)
+
+func (s *scanner) scanEach() *token {
+	if sm := rgxEach.FindStringSubmatch(s.buffer); len(sm) != 0 {
+		s.consume(len(sm[0]))
+		return &token{tokEach, sm[3], map[string]string{"X": sm[1], "Y": sm[2]}}
+	}
+
+	return nil
+}
+
+var rgxAssignment = regexp.MustCompile(`^(\$[\w0-9\-_]*)?\s*=\s*(.+)$`)
+
+func (s *scanner) scanAssignment() *token {
+	if sm := rgxAssignment.FindStringSubmatch(s.buffer); len(sm) != 0 {
+		s.consume(len(sm[0]))
+		return &token{tokAssignment, sm[2], map[string]string{"X": sm[1]}}
+	}
+
+	return nil
+}
+
+var rgxComment = regexp.MustCompile(`^\/\/(-)?\s*(.*)$`)
+
+func (s *scanner) scanComment() *token {
+	if sm := rgxComment.FindStringSubmatch(s.buffer); len(sm) != 0 {
+		mode := "embed"
+		if len(sm[1]) != 0 {
+			mode = "silent"
+		}
+
+		s.consume(len(sm[0]))
+		return &token{tokComment, sm[2], map[string]string{"Mode": mode}}
+	}
+
+	return nil
+}
+
+var rgxId = regexp.MustCompile(`^#([\w-]+)(?:\s*\?\s*(.*)$)?`)
+
+func (s *scanner) scanId() *token {
+	if sm := rgxId.FindStringSubmatch(s.buffer); len(sm) != 0 {
+		s.consume(len(sm[0]))
+		return &token{tokId, sm[1], map[string]string{"Condition": sm[2]}}
+	}
+
+	return nil
+}
+
+var rgxClassName = regexp.MustCompile(`^\.([\w-]+)(?:\s*\?\s*(.*)$)?`)
+
+func (s *scanner) scanClassName() *token {
+	if sm := rgxClassName.FindStringSubmatch(s.buffer); len(sm) != 0 {
+		s.consume(len(sm[0]))
+		return &token{tokClassName, sm[1], map[string]string{"Condition": sm[2]}}
+	}
+
+	return nil
+}
+
+var rgxAttribute = regexp.MustCompile(`^\[([\w\-:@\.]+)\s*(?:=\s*(\"([^\"\\]*)\"|([^\]]+)))?\](?:\s*\?\s*(.*)$)?`)
+
+func (s *scanner) scanAttribute() *token {
+	if sm := rgxAttribute.FindStringSubmatch(s.buffer); len(sm) != 0 {
+		s.consume(len(sm[0]))
+
+		if len(sm[3]) != 0 || sm[2] == "" {
+			return &token{tokAttribute, sm[1], map[string]string{"Content": sm[3], "Mode": "raw", "Condition": sm[5]}}
+		}
+
+		return &token{tokAttribute, sm[1], map[string]string{"Content": sm[4], "Mode": "expression", "Condition": sm[5]}}
+	}
+
+	return nil
+}
+
+var rgxImport = regexp.MustCompile(`^import\s+([0-9a-zA-Z_\-\. \/]*)$`)
+
+func (s *scanner) scanImport() *token {
+	if sm := rgxImport.FindStringSubmatch(s.buffer); len(sm) != 0 {
+		s.consume(len(sm[0]))
+		return &token{tokImport, sm[1], nil}
+	}
+
+	return nil
+}
+
+var rgxExtends = regexp.MustCompile(`^extends\s+([0-9a-zA-Z_\-\. \/]*)$`)
+
+func (s *scanner) scanExtends() *token {
+	if sm := rgxExtends.FindStringSubmatch(s.buffer); len(sm) != 0 {
+		s.consume(len(sm[0]))
+		return &token{tokExtends, sm[1], nil}
+	}
+
+	return nil
+}
+
+var rgxBlock = regexp.MustCompile(`^block\s+(?:(append|prepend)\s+)?([0-9a-zA-Z_\-\. \/]*)$`)
+
+func (s *scanner) scanBlock() *token {
+	if sm := rgxBlock.FindStringSubmatch(s.buffer); len(sm) != 0 {
+		s.consume(len(sm[0]))
+		return &token{tokNamedBlock, sm[2], map[string]string{"Modifier": sm[1]}}
+	}
+
+	return nil
+}
+
+var rgxTag = regexp.MustCompile(`^(\w[-:\w]*)`)
+
+func (s *scanner) scanTag() *token {
+	if sm := rgxTag.FindStringSubmatch(s.buffer); len(sm) != 0 {
+		s.consume(len(sm[0]))
+		return &token{tokTag, sm[1], nil}
+	}
+
+	return nil
+}
+
+var rgxMixin = regexp.MustCompile(`^mixin ([a-zA-Z_-]+\w*)(\(((\$\w*(,\s)?)*)\))?$`)
+
+func (s *scanner) scanMixin() *token {
+	if sm := rgxMixin.FindStringSubmatch(s.buffer); len(sm) != 0 {
+		s.consume(len(sm[0]))
+		return &token{tokMixin, sm[1], map[string]string{"Args": sm[3]}}
+	}
+
+	return nil
+}
+
+var rgxMixinCall = regexp.MustCompile(`^\+([A-Za-z_-]+\w*)(\((.+(,\s)?)*\))?$`)
+
+func (s *scanner) scanMixinCall() *token {
+	if sm := rgxMixinCall.FindStringSubmatch(s.buffer); len(sm) != 0 {
+		s.consume(len(sm[0]))
+		return &token{tokMixinCall, sm[1], map[string]string{"Args": sm[3]}}
+	}
+
+	return nil
+}
+
+var rgxText = regexp.MustCompile(`^(\|)? ?(.*)$`)
+
+func (s *scanner) scanText() *token {
+	if sm := rgxText.FindStringSubmatch(s.buffer); len(sm) != 0 {
+		s.consume(len(sm[0]))
+
+		mode := "inline"
+		if sm[1] == "|" {
+			mode = "piped"
+		}
+
+		return &token{tokText, sm[2], map[string]string{"Mode": mode}}
+	}
+
+	return nil
+}
+
+// Moves position forward, and removes beginning of s.buffer (len bytes)
+func (s *scanner) consume(runes int) {
+	if len(s.buffer) < runes {
+		panic(fmt.Sprintf("Unable to consume %d runes from buffer.", runes))
+	}
+
+	s.lastTokenLine = s.line
+	s.lastTokenCol = s.col
+	s.lastTokenSize = runes
+
+	s.buffer = s.buffer[runes:]
+	s.col += runes
+}
+
+// Reads string into s.buffer
+func (s *scanner) ensureBuffer() {
+	if len(s.buffer) > 0 {
+		return
+	}
+
+	buf, err := s.reader.ReadString('\n')
+
+	if err != nil && err != io.EOF {
+		panic(err)
+	} else if err != nil && len(buf) == 0 {
+		s.state = scnEOF
+	} else {
+		// endline "LF only" or "\n" use Unix, Linux, modern MacOS X, FreeBSD, BeOS, RISC OS
+		if buf[len(buf)-1] == '\n' {
+			buf = buf[:len(buf)-1]
+		}
+		// endline "CR+LF" or "\r\n" use internet protocols, DEC RT-11, Windows, CP/M, MS-DOS, OS/2, Symbian OS
+		if len(buf) > 0 && buf[len(buf)-1] == '\r' {
+			buf = buf[:len(buf)-1]
+		}
+
+		s.state = scnNewLine
+		s.buffer = buf
+		s.line += 1
+		s.col = 0
+	}
+}