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

import (
	"bufio"
5
	"bytes"
6
	"errors"
7
	"io"
Michael Yang's avatar
Michael Yang committed
8
	"log"
9
10
11
12
)

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
	var command, modelCommand Command
24
25

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

		fields := bytes.SplitN(line, []byte(" "), 2)
Michael Yang's avatar
Michael Yang committed
31
		if len(fields) == 0 || len(fields[0]) == 0 {
32
33
34
			continue
		}

35
		switch string(bytes.ToUpper(fields[0])) {
36
37
		case "FROM":
			command.Name = "model"
38
39
40
			command.Args = string(fields[1])
			// copy command for validation
			modelCommand = command
Jeffrey Morgan's avatar
Jeffrey Morgan committed
41
		case "LICENSE", "TEMPLATE", "SYSTEM", "PROMPT":
42
			command.Name = string(bytes.ToLower(fields[0]))
43
			command.Args = string(fields[1])
44
		case "PARAMETER":
45
46
47
			fields = bytes.SplitN(fields[1], []byte(" "), 2)
			command.Name = string(fields[0])
			command.Args = string(fields[1])
48
		default:
49
			// log a warning for unknown commands
Michael Yang's avatar
Michael Yang committed
50
			log.Printf("WARNING: Unknown command: %s", fields[0])
51
			continue
52
		}
53
54
55

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

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

	return commands, scanner.Err()
}
64
65

func scanModelfile(data []byte, atEOF bool) (advance int, token []byte, err error) {
66
67
	const multilineString = `"""`

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

70
71
	if start := bytes.Index(data, []byte(multilineString)); start >= 0 && start < newline {
		end := bytes.Index(data[start+len(multilineString):], []byte(multilineString))
72
		if end < 0 {
73
			if atEOF {
74
				return 0, nil, errors.New("unterminated multiline string: " + multilineString)
75
76
77
			} else {
				return 0, nil, nil
			}
78
79
		}

80
		n := start + len(multilineString) + end + len(multilineString)
Michael Yang's avatar
Michael Yang committed
81
82
83
84

		newData := data[:start]
		newData = append(newData, data[start+len(multilineString):n-len(multilineString)]...)
		return n, newData, nil
85
86
87
88
	}

	return bufio.ScanLines(data, atEOF)
}