aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/homemade/scl/scanner.go
blob: 7dddf59edc43538deee6912ad20a4c9931d96e35 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package scl

import (
	"bufio"
	"fmt"
	"io"
	"strings"
)

type scannerTree []*scannerLine

type scanner struct {
	file   string
	reader io.Reader
	lines  scannerTree
}

func newScanner(reader io.Reader, filename ...string) *scanner {

	file := "<no file>"

	if len(filename) > 0 {
		file = filename[0]
	}

	s := scanner{
		file:   file,
		reader: reader,
		lines:  make(scannerTree, 0),
	}

	return &s
}

func (s *scanner) scan() (lines scannerTree, err error) {

	// Split to lines
	scanner := bufio.NewScanner(s.reader)
	scanner.Split(bufio.ScanLines)

	lineNumber := 0
	rawLines := make(scannerTree, 0)

	heredoc := ""
	heredocContent := ""
	heredocLine := 0

	for scanner.Scan() {
		lineNumber++

		if heredoc != "" {
			heredocContent += "\n" + scanner.Text()

			if strings.TrimSpace(scanner.Text()) == heredoc {
				// HCL requires heredocs to be terminated with a newline
				rawLines = append(rawLines, newLine(s.file, lineNumber, 0, heredocContent+"\n"))
				heredoc = ""
				heredocContent = ""
			}

			continue
		}

		text := strings.TrimRight(scanner.Text(), " \t{}")

		if text == "" {
			continue
		}

		if matches := heredocMatcher.FindAllStringSubmatch(text, -1); matches != nil {
			heredoc = matches[0][1]
			heredocContent = text
			heredocLine = lineNumber
			continue
		}

		rawLines = append(rawLines, newLine(s.file, lineNumber, 0, text))
	}

	if heredoc != "" {
		return lines, fmt.Errorf("Heredoc '%s' (started line %d) not terminated", heredoc, heredocLine)
	}

	// Make sure the first line has no indent
	if len(rawLines) > 0 {
		index := 0
		s.indentLines(&index, rawLines, &lines, rawLines[0].content.indent())
	}

	return
}

func (s *scanner) indentLines(index *int, input scannerTree, output *scannerTree, indent int) {

	// Ends when there are no more lines
	if *index >= len(input) {
		return
	}

	var lineToAdd *scannerLine

	for ; *index < len(input); *index++ {

		lineIndent := input[*index].content.indent()

		if lineIndent == indent {
			lineToAdd = input[*index].branch()
			*output = append(*output, lineToAdd)

		} else if lineIndent > indent {
			s.indentLines(index, input, &lineToAdd.children, lineIndent)

		} else if lineIndent < indent {
			*index--
			return
		}

	}

	return
}