parser.go 1.98 KB
Newer Older
1
2
3
4
package parser

import (
	"bufio"
5
6
	"bytes"
	"errors"
7
8
9
10
11
12
	"fmt"
	"io"
)

type Command struct {
	Name string
13
14
15
16
17
18
	Args string
}

func (c *Command) Reset() {
	c.Name = ""
	c.Args = ""
19
20
21
22
}

func Parse(reader io.Reader) ([]Command, error) {
	var commands []Command
23
24

	var command, modelCommand Command
25
26

	scanner := bufio.NewScanner(reader)
27
	scanner.Split(scanModelfile)
28
	for scanner.Scan() {
29
30
31
		line := scanner.Bytes()

		fields := bytes.SplitN(line, []byte(" "), 2)
32
33
34
35
		if len(fields) == 0 {
			continue
		}

36
		switch string(bytes.ToUpper(fields[0])) {
37
38
		case "FROM":
			command.Name = "model"
39
40
41
42
43
44
			command.Args = string(fields[1])
			// copy command for validation
			modelCommand = command
		case "LICENSE", "TEMPLATE", "SYSTEM":
			command.Name = string(bytes.ToLower(fields[0]))
			command.Args = string(fields[1])
45
		case "PARAMETER":
46
47
48
			fields = bytes.SplitN(fields[1], []byte(" "), 2)
			command.Name = string(fields[0])
			command.Args = string(fields[1])
49
50
51
		default:
			continue
		}
52
53
54

		commands = append(commands, command)
		command.Reset()
55
56
	}

57
	if modelCommand.Args == "" {
58
59
60
61
62
		return nil, fmt.Errorf("no FROM line for the model was specified")
	}

	return commands, scanner.Err()
}
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

func scanModelfile(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF || len(data) == 0 {
		return 0, nil, nil
	}

	newline := bytes.IndexByte(data, '\n')

	if start := bytes.Index(data, []byte(`"""`)); start >= 0 && start < newline {
		end := bytes.Index(data[start+3:], []byte(`"""`))
		if end < 0 {
			return 0, nil, errors.New(`unterminated multiline string: """`)
		}

		n := start + 3 + end + 3
		return n, bytes.Replace(data[:n], []byte(`"""`), []byte(""), 2), nil
	}

	if start := bytes.Index(data, []byte(`'''`)); start >= 0 && start < newline {
		end := bytes.Index(data[start+3:], []byte(`'''`))
		if end < 0 {
			return 0, nil, errors.New("unterminated multiline string: '''")
		}

		n := start + 3 + end + 3
		return n, bytes.Replace(data[:n], []byte("'''"), []byte(""), 2), nil
	}

	return bufio.ScanLines(data, atEOF)
}