1fd4e5da5Sopenharmony_ci// Copyright (C) 2019 Google Inc.
2fd4e5da5Sopenharmony_ci//
3fd4e5da5Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License");
4fd4e5da5Sopenharmony_ci// you may not use this file except in compliance with the License.
5fd4e5da5Sopenharmony_ci// You may obtain a copy of the License at
6fd4e5da5Sopenharmony_ci//
7fd4e5da5Sopenharmony_ci//      http://www.apache.org/licenses/LICENSE-2.0
8fd4e5da5Sopenharmony_ci//
9fd4e5da5Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software
10fd4e5da5Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS,
11fd4e5da5Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12fd4e5da5Sopenharmony_ci// See the License for the specific language governing permissions and
13fd4e5da5Sopenharmony_ci// limitations under the License.
14fd4e5da5Sopenharmony_ci
15fd4e5da5Sopenharmony_ci// Package parser implements a SPIR-V assembly parser.
16fd4e5da5Sopenharmony_cipackage parser
17fd4e5da5Sopenharmony_ci
18fd4e5da5Sopenharmony_ciimport (
19fd4e5da5Sopenharmony_ci	"fmt"
20fd4e5da5Sopenharmony_ci	"io"
21fd4e5da5Sopenharmony_ci	"log"
22fd4e5da5Sopenharmony_ci	"strings"
23fd4e5da5Sopenharmony_ci	"unicode"
24fd4e5da5Sopenharmony_ci	"unicode/utf8"
25fd4e5da5Sopenharmony_ci
26fd4e5da5Sopenharmony_ci	"github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/schema"
27fd4e5da5Sopenharmony_ci)
28fd4e5da5Sopenharmony_ci
29fd4e5da5Sopenharmony_ci// Type is an enumerator of token types.
30fd4e5da5Sopenharmony_citype Type int
31fd4e5da5Sopenharmony_ci
32fd4e5da5Sopenharmony_ci// Type enumerators
33fd4e5da5Sopenharmony_ciconst (
34fd4e5da5Sopenharmony_ci	Ident  Type = iota // Foo
35fd4e5da5Sopenharmony_ci	PIdent             // %32, %foo
36fd4e5da5Sopenharmony_ci	Integer
37fd4e5da5Sopenharmony_ci	Float
38fd4e5da5Sopenharmony_ci	String
39fd4e5da5Sopenharmony_ci	Operator
40fd4e5da5Sopenharmony_ci	Comment
41fd4e5da5Sopenharmony_ci	Newline
42fd4e5da5Sopenharmony_ci)
43fd4e5da5Sopenharmony_ci
44fd4e5da5Sopenharmony_cifunc (t Type) String() string {
45fd4e5da5Sopenharmony_ci	switch t {
46fd4e5da5Sopenharmony_ci	case Ident:
47fd4e5da5Sopenharmony_ci		return "Ident"
48fd4e5da5Sopenharmony_ci	case PIdent:
49fd4e5da5Sopenharmony_ci		return "PIdent"
50fd4e5da5Sopenharmony_ci	case Integer:
51fd4e5da5Sopenharmony_ci		return "Integer"
52fd4e5da5Sopenharmony_ci	case Float:
53fd4e5da5Sopenharmony_ci		return "Float"
54fd4e5da5Sopenharmony_ci	case String:
55fd4e5da5Sopenharmony_ci		return "String"
56fd4e5da5Sopenharmony_ci	case Operator:
57fd4e5da5Sopenharmony_ci		return "Operator"
58fd4e5da5Sopenharmony_ci	case Comment:
59fd4e5da5Sopenharmony_ci		return "Comment"
60fd4e5da5Sopenharmony_ci	default:
61fd4e5da5Sopenharmony_ci		return "<unknown>"
62fd4e5da5Sopenharmony_ci	}
63fd4e5da5Sopenharmony_ci}
64fd4e5da5Sopenharmony_ci
65fd4e5da5Sopenharmony_ci// Token represents a single lexed token.
66fd4e5da5Sopenharmony_citype Token struct {
67fd4e5da5Sopenharmony_ci	Type  Type
68fd4e5da5Sopenharmony_ci	Range Range
69fd4e5da5Sopenharmony_ci}
70fd4e5da5Sopenharmony_ci
71fd4e5da5Sopenharmony_cifunc (t Token) String() string { return fmt.Sprintf("{%v %v}", t.Type, t.Range) }
72fd4e5da5Sopenharmony_ci
73fd4e5da5Sopenharmony_ci// Text returns the tokens text from the source.
74fd4e5da5Sopenharmony_cifunc (t Token) Text(lines []string) string { return t.Range.Text(lines) }
75fd4e5da5Sopenharmony_ci
76fd4e5da5Sopenharmony_ci// Range represents an interval in a text file.
77fd4e5da5Sopenharmony_citype Range struct {
78fd4e5da5Sopenharmony_ci	Start Position
79fd4e5da5Sopenharmony_ci	End   Position
80fd4e5da5Sopenharmony_ci}
81fd4e5da5Sopenharmony_ci
82fd4e5da5Sopenharmony_cifunc (r Range) String() string { return fmt.Sprintf("[%v %v]", r.Start, r.End) }
83fd4e5da5Sopenharmony_ci
84fd4e5da5Sopenharmony_ci// Text returns the text for the given Range in the provided lines.
85fd4e5da5Sopenharmony_cifunc (r Range) Text(lines []string) string {
86fd4e5da5Sopenharmony_ci	sl, sc := r.Start.Line-1, r.Start.Column-1
87fd4e5da5Sopenharmony_ci	if sl < 0 || sc < 0 || sl > len(lines) || sc > len(lines[sl]) {
88fd4e5da5Sopenharmony_ci		return fmt.Sprintf("<invalid start position %v>", r.Start)
89fd4e5da5Sopenharmony_ci	}
90fd4e5da5Sopenharmony_ci	el, ec := r.End.Line-1, r.End.Column-1
91fd4e5da5Sopenharmony_ci	if el < 0 || ec < 0 || el > len(lines) || ec > len(lines[sl]) {
92fd4e5da5Sopenharmony_ci		return fmt.Sprintf("<invalid end position %v>", r.End)
93fd4e5da5Sopenharmony_ci	}
94fd4e5da5Sopenharmony_ci
95fd4e5da5Sopenharmony_ci	sb := strings.Builder{}
96fd4e5da5Sopenharmony_ci	if sl != el {
97fd4e5da5Sopenharmony_ci		sb.WriteString(lines[sl][sc:])
98fd4e5da5Sopenharmony_ci		for l := sl + 1; l < el; l++ {
99fd4e5da5Sopenharmony_ci			sb.WriteString(lines[l])
100fd4e5da5Sopenharmony_ci		}
101fd4e5da5Sopenharmony_ci		sb.WriteString(lines[el][:ec])
102fd4e5da5Sopenharmony_ci	} else {
103fd4e5da5Sopenharmony_ci		sb.WriteString(lines[sl][sc:ec])
104fd4e5da5Sopenharmony_ci	}
105fd4e5da5Sopenharmony_ci	return sb.String()
106fd4e5da5Sopenharmony_ci}
107fd4e5da5Sopenharmony_ci
108fd4e5da5Sopenharmony_ci// Contains returns true if p is in r.
109fd4e5da5Sopenharmony_cifunc (r Range) Contains(p Position) bool {
110fd4e5da5Sopenharmony_ci	return !(p.LessThan(r.Start) || p.GreaterThan(r.End))
111fd4e5da5Sopenharmony_ci}
112fd4e5da5Sopenharmony_ci
113fd4e5da5Sopenharmony_cifunc (r *Range) grow(o Range) {
114fd4e5da5Sopenharmony_ci	if !r.Start.IsValid() || o.Start.LessThan(r.Start) {
115fd4e5da5Sopenharmony_ci		r.Start = o.Start
116fd4e5da5Sopenharmony_ci	}
117fd4e5da5Sopenharmony_ci	if !r.End.IsValid() || o.End.GreaterThan(r.End) {
118fd4e5da5Sopenharmony_ci		r.End = o.End
119fd4e5da5Sopenharmony_ci	}
120fd4e5da5Sopenharmony_ci}
121fd4e5da5Sopenharmony_ci
122fd4e5da5Sopenharmony_ci// Position holds a line and column position in a text file.
123fd4e5da5Sopenharmony_citype Position struct {
124fd4e5da5Sopenharmony_ci	Line, Column int
125fd4e5da5Sopenharmony_ci}
126fd4e5da5Sopenharmony_ci
127fd4e5da5Sopenharmony_cifunc (p Position) String() string { return fmt.Sprintf("%v:%v", p.Line, p.Column) }
128fd4e5da5Sopenharmony_ci
129fd4e5da5Sopenharmony_ci// IsValid returns true if the position has a line and column greater than 1.
130fd4e5da5Sopenharmony_cifunc (p Position) IsValid() bool { return p.Line > 0 && p.Column > 0 }
131fd4e5da5Sopenharmony_ci
132fd4e5da5Sopenharmony_ci// LessThan returns true iff o is before p.
133fd4e5da5Sopenharmony_cifunc (p Position) LessThan(o Position) bool {
134fd4e5da5Sopenharmony_ci	switch {
135fd4e5da5Sopenharmony_ci	case !p.IsValid() || !o.IsValid():
136fd4e5da5Sopenharmony_ci		return false
137fd4e5da5Sopenharmony_ci	case p.Line < o.Line:
138fd4e5da5Sopenharmony_ci		return true
139fd4e5da5Sopenharmony_ci	case p.Line > o.Line:
140fd4e5da5Sopenharmony_ci		return false
141fd4e5da5Sopenharmony_ci	case p.Column < o.Column:
142fd4e5da5Sopenharmony_ci		return true
143fd4e5da5Sopenharmony_ci	default:
144fd4e5da5Sopenharmony_ci		return false
145fd4e5da5Sopenharmony_ci	}
146fd4e5da5Sopenharmony_ci}
147fd4e5da5Sopenharmony_ci
148fd4e5da5Sopenharmony_ci// GreaterThan returns true iff o is greater than p.
149fd4e5da5Sopenharmony_cifunc (p Position) GreaterThan(o Position) bool {
150fd4e5da5Sopenharmony_ci	switch {
151fd4e5da5Sopenharmony_ci	case !p.IsValid() || !o.IsValid():
152fd4e5da5Sopenharmony_ci		return false
153fd4e5da5Sopenharmony_ci	case p.Line > o.Line:
154fd4e5da5Sopenharmony_ci		return true
155fd4e5da5Sopenharmony_ci	case p.Line < o.Line:
156fd4e5da5Sopenharmony_ci		return false
157fd4e5da5Sopenharmony_ci	case p.Column > o.Column:
158fd4e5da5Sopenharmony_ci		return true
159fd4e5da5Sopenharmony_ci	default:
160fd4e5da5Sopenharmony_ci		return false
161fd4e5da5Sopenharmony_ci	}
162fd4e5da5Sopenharmony_ci}
163fd4e5da5Sopenharmony_ci
164fd4e5da5Sopenharmony_citype lexer struct {
165fd4e5da5Sopenharmony_ci	source string
166fd4e5da5Sopenharmony_ci	lexerState
167fd4e5da5Sopenharmony_ci	diags []Diagnostic
168fd4e5da5Sopenharmony_ci	e     error
169fd4e5da5Sopenharmony_ci}
170fd4e5da5Sopenharmony_ci
171fd4e5da5Sopenharmony_citype lexerState struct {
172fd4e5da5Sopenharmony_ci	offset int      // byte offset in source
173fd4e5da5Sopenharmony_ci	toks   []*Token // all the lexed tokens
174fd4e5da5Sopenharmony_ci	pos    Position // current position
175fd4e5da5Sopenharmony_ci}
176fd4e5da5Sopenharmony_ci
177fd4e5da5Sopenharmony_ci// err appends an fmt.Printf style error into l.diags for the given token.
178fd4e5da5Sopenharmony_cifunc (l *lexer) err(tok *Token, msg string, args ...interface{}) {
179fd4e5da5Sopenharmony_ci	rng := Range{}
180fd4e5da5Sopenharmony_ci	if tok != nil {
181fd4e5da5Sopenharmony_ci		rng = tok.Range
182fd4e5da5Sopenharmony_ci	}
183fd4e5da5Sopenharmony_ci	l.diags = append(l.diags, Diagnostic{
184fd4e5da5Sopenharmony_ci		Range:    rng,
185fd4e5da5Sopenharmony_ci		Severity: SeverityError,
186fd4e5da5Sopenharmony_ci		Message:  fmt.Sprintf(msg, args...),
187fd4e5da5Sopenharmony_ci	})
188fd4e5da5Sopenharmony_ci}
189fd4e5da5Sopenharmony_ci
190fd4e5da5Sopenharmony_ci// next returns the next rune, or io.EOF if the last rune has already been
191fd4e5da5Sopenharmony_ci// consumed.
192fd4e5da5Sopenharmony_cifunc (l *lexer) next() rune {
193fd4e5da5Sopenharmony_ci	if l.offset >= len(l.source) {
194fd4e5da5Sopenharmony_ci		l.e = io.EOF
195fd4e5da5Sopenharmony_ci		return 0
196fd4e5da5Sopenharmony_ci	}
197fd4e5da5Sopenharmony_ci	r, n := utf8.DecodeRuneInString(l.source[l.offset:])
198fd4e5da5Sopenharmony_ci	l.offset += n
199fd4e5da5Sopenharmony_ci	if n == 0 {
200fd4e5da5Sopenharmony_ci		l.e = io.EOF
201fd4e5da5Sopenharmony_ci		return 0
202fd4e5da5Sopenharmony_ci	}
203fd4e5da5Sopenharmony_ci	if r == '\n' {
204fd4e5da5Sopenharmony_ci		l.pos.Line++
205fd4e5da5Sopenharmony_ci		l.pos.Column = 1
206fd4e5da5Sopenharmony_ci	} else {
207fd4e5da5Sopenharmony_ci		l.pos.Column++
208fd4e5da5Sopenharmony_ci	}
209fd4e5da5Sopenharmony_ci	return r
210fd4e5da5Sopenharmony_ci}
211fd4e5da5Sopenharmony_ci
212fd4e5da5Sopenharmony_ci// save returns the current lexerState.
213fd4e5da5Sopenharmony_cifunc (l *lexer) save() lexerState {
214fd4e5da5Sopenharmony_ci	return l.lexerState
215fd4e5da5Sopenharmony_ci}
216fd4e5da5Sopenharmony_ci
217fd4e5da5Sopenharmony_ci// restore restores the current lexer state with s.
218fd4e5da5Sopenharmony_cifunc (l *lexer) restore(s lexerState) {
219fd4e5da5Sopenharmony_ci	l.lexerState = s
220fd4e5da5Sopenharmony_ci}
221fd4e5da5Sopenharmony_ci
222fd4e5da5Sopenharmony_ci// pident processes the PIdent token at the current position.
223fd4e5da5Sopenharmony_ci// The lexer *must* know the next token is a PIdent before calling.
224fd4e5da5Sopenharmony_cifunc (l *lexer) pident() {
225fd4e5da5Sopenharmony_ci	tok := &Token{Type: PIdent, Range: Range{Start: l.pos, End: l.pos}}
226fd4e5da5Sopenharmony_ci	if r := l.next(); r != '%' {
227fd4e5da5Sopenharmony_ci		log.Fatalf("lexer expected '%%', got '%v'", r)
228fd4e5da5Sopenharmony_ci		return
229fd4e5da5Sopenharmony_ci	}
230fd4e5da5Sopenharmony_ci	for l.e == nil {
231fd4e5da5Sopenharmony_ci		s := l.save()
232fd4e5da5Sopenharmony_ci		r := l.next()
233fd4e5da5Sopenharmony_ci		if !isAlphaNumeric(r) && r != '_' {
234fd4e5da5Sopenharmony_ci			l.restore(s)
235fd4e5da5Sopenharmony_ci			break
236fd4e5da5Sopenharmony_ci		}
237fd4e5da5Sopenharmony_ci	}
238fd4e5da5Sopenharmony_ci	tok.Range.End = l.pos
239fd4e5da5Sopenharmony_ci	l.toks = append(l.toks, tok)
240fd4e5da5Sopenharmony_ci}
241fd4e5da5Sopenharmony_ci
242fd4e5da5Sopenharmony_ci// numberOrIdent processes the Ident, Float or Integer token at the current
243fd4e5da5Sopenharmony_ci// position.
244fd4e5da5Sopenharmony_cifunc (l *lexer) numberOrIdent() {
245fd4e5da5Sopenharmony_ci	const Unknown Type = -1
246fd4e5da5Sopenharmony_ci	tok := &Token{Type: Unknown, Range: Range{Start: l.pos, End: l.pos}}
247fd4e5da5Sopenharmony_ciloop:
248fd4e5da5Sopenharmony_ci	for l.e == nil {
249fd4e5da5Sopenharmony_ci		s := l.save()
250fd4e5da5Sopenharmony_ci		r := l.next()
251fd4e5da5Sopenharmony_ci		switch {
252fd4e5da5Sopenharmony_ci		case r == '-', r == '+', isNumeric(r):
253fd4e5da5Sopenharmony_ci			continue
254fd4e5da5Sopenharmony_ci		case isAlpha(r), r == '_':
255fd4e5da5Sopenharmony_ci			switch tok.Type {
256fd4e5da5Sopenharmony_ci			case Unknown:
257fd4e5da5Sopenharmony_ci				tok.Type = Ident
258fd4e5da5Sopenharmony_ci			case Float, Integer:
259fd4e5da5Sopenharmony_ci				l.err(tok, "invalid number")
260fd4e5da5Sopenharmony_ci				return
261fd4e5da5Sopenharmony_ci			}
262fd4e5da5Sopenharmony_ci		case r == '.':
263fd4e5da5Sopenharmony_ci			switch tok.Type {
264fd4e5da5Sopenharmony_ci			case Unknown:
265fd4e5da5Sopenharmony_ci				tok.Type = Float
266fd4e5da5Sopenharmony_ci			default:
267fd4e5da5Sopenharmony_ci				l.restore(s)
268fd4e5da5Sopenharmony_ci				break loop
269fd4e5da5Sopenharmony_ci			}
270fd4e5da5Sopenharmony_ci		default:
271fd4e5da5Sopenharmony_ci			if tok.Type == Unknown {
272fd4e5da5Sopenharmony_ci				tok.Type = Integer
273fd4e5da5Sopenharmony_ci			}
274fd4e5da5Sopenharmony_ci			l.restore(s)
275fd4e5da5Sopenharmony_ci			break loop
276fd4e5da5Sopenharmony_ci		}
277fd4e5da5Sopenharmony_ci	}
278fd4e5da5Sopenharmony_ci	tok.Range.End = l.pos
279fd4e5da5Sopenharmony_ci	l.toks = append(l.toks, tok)
280fd4e5da5Sopenharmony_ci}
281fd4e5da5Sopenharmony_ci
282fd4e5da5Sopenharmony_ci// string processes the String token at the current position.
283fd4e5da5Sopenharmony_ci// The lexer *must* know the next token is a String before calling.
284fd4e5da5Sopenharmony_cifunc (l *lexer) string() {
285fd4e5da5Sopenharmony_ci	tok := &Token{Type: String, Range: Range{Start: l.pos, End: l.pos}}
286fd4e5da5Sopenharmony_ci	if r := l.next(); r != '"' {
287fd4e5da5Sopenharmony_ci		log.Fatalf("lexer expected '\"', got '%v'", r)
288fd4e5da5Sopenharmony_ci		return
289fd4e5da5Sopenharmony_ci	}
290fd4e5da5Sopenharmony_ci	escape := false
291fd4e5da5Sopenharmony_ci	for l.e == nil {
292fd4e5da5Sopenharmony_ci		switch l.next() {
293fd4e5da5Sopenharmony_ci		case '"':
294fd4e5da5Sopenharmony_ci			if !escape {
295fd4e5da5Sopenharmony_ci				tok.Range.End = l.pos
296fd4e5da5Sopenharmony_ci				l.toks = append(l.toks, tok)
297fd4e5da5Sopenharmony_ci				return
298fd4e5da5Sopenharmony_ci			}
299fd4e5da5Sopenharmony_ci		case '\\':
300fd4e5da5Sopenharmony_ci			escape = !escape
301fd4e5da5Sopenharmony_ci		default:
302fd4e5da5Sopenharmony_ci			escape = false
303fd4e5da5Sopenharmony_ci		}
304fd4e5da5Sopenharmony_ci	}
305fd4e5da5Sopenharmony_ci}
306fd4e5da5Sopenharmony_ci
307fd4e5da5Sopenharmony_ci// operator processes the Operator token at the current position.
308fd4e5da5Sopenharmony_ci// The lexer *must* know the next token is a Operator before calling.
309fd4e5da5Sopenharmony_cifunc (l *lexer) operator() {
310fd4e5da5Sopenharmony_ci	tok := &Token{Type: Operator, Range: Range{Start: l.pos, End: l.pos}}
311fd4e5da5Sopenharmony_ci	for l.e == nil {
312fd4e5da5Sopenharmony_ci		switch l.next() {
313fd4e5da5Sopenharmony_ci		case '=', '|':
314fd4e5da5Sopenharmony_ci			tok.Range.End = l.pos
315fd4e5da5Sopenharmony_ci			l.toks = append(l.toks, tok)
316fd4e5da5Sopenharmony_ci			return
317fd4e5da5Sopenharmony_ci		}
318fd4e5da5Sopenharmony_ci	}
319fd4e5da5Sopenharmony_ci}
320fd4e5da5Sopenharmony_ci
321fd4e5da5Sopenharmony_ci// lineComment processes the Comment token at the current position.
322fd4e5da5Sopenharmony_ci// The lexer *must* know the next token is a Comment before calling.
323fd4e5da5Sopenharmony_cifunc (l *lexer) lineComment() {
324fd4e5da5Sopenharmony_ci	tok := &Token{Type: Comment, Range: Range{Start: l.pos, End: l.pos}}
325fd4e5da5Sopenharmony_ci	if r := l.next(); r != ';' {
326fd4e5da5Sopenharmony_ci		log.Fatalf("lexer expected ';', got '%v'", r)
327fd4e5da5Sopenharmony_ci		return
328fd4e5da5Sopenharmony_ci	}
329fd4e5da5Sopenharmony_ci	for l.e == nil {
330fd4e5da5Sopenharmony_ci		s := l.save()
331fd4e5da5Sopenharmony_ci		switch l.next() {
332fd4e5da5Sopenharmony_ci		case '\n':
333fd4e5da5Sopenharmony_ci			l.restore(s)
334fd4e5da5Sopenharmony_ci			tok.Range.End = l.pos
335fd4e5da5Sopenharmony_ci			l.toks = append(l.toks, tok)
336fd4e5da5Sopenharmony_ci			return
337fd4e5da5Sopenharmony_ci		}
338fd4e5da5Sopenharmony_ci	}
339fd4e5da5Sopenharmony_ci}
340fd4e5da5Sopenharmony_ci
341fd4e5da5Sopenharmony_ci// newline processes the Newline token at the current position.
342fd4e5da5Sopenharmony_ci// The lexer *must* know the next token is a Newline before calling.
343fd4e5da5Sopenharmony_cifunc (l *lexer) newline() {
344fd4e5da5Sopenharmony_ci	tok := &Token{Type: Newline, Range: Range{Start: l.pos, End: l.pos}}
345fd4e5da5Sopenharmony_ci	if r := l.next(); r != '\n' {
346fd4e5da5Sopenharmony_ci		log.Fatalf("lexer expected '\n', got '%v'", r)
347fd4e5da5Sopenharmony_ci		return
348fd4e5da5Sopenharmony_ci	}
349fd4e5da5Sopenharmony_ci	tok.Range.End = l.pos
350fd4e5da5Sopenharmony_ci	l.toks = append(l.toks, tok)
351fd4e5da5Sopenharmony_ci}
352fd4e5da5Sopenharmony_ci
353fd4e5da5Sopenharmony_ci// lex returns all the tokens and diagnostics after lexing source.
354fd4e5da5Sopenharmony_cifunc lex(source string) ([]*Token, []Diagnostic, error) {
355fd4e5da5Sopenharmony_ci	l := lexer{source: source, lexerState: lexerState{pos: Position{1, 1}}}
356fd4e5da5Sopenharmony_ci
357fd4e5da5Sopenharmony_ci	lastPos := Position{}
358fd4e5da5Sopenharmony_ci	for l.e == nil {
359fd4e5da5Sopenharmony_ci		// Integrity check that the parser is making progress
360fd4e5da5Sopenharmony_ci		if l.pos == lastPos {
361fd4e5da5Sopenharmony_ci			log.Panicf("Parsing stuck at %v", l.pos)
362fd4e5da5Sopenharmony_ci		}
363fd4e5da5Sopenharmony_ci		lastPos = l.pos
364fd4e5da5Sopenharmony_ci
365fd4e5da5Sopenharmony_ci		s := l.save()
366fd4e5da5Sopenharmony_ci		r := l.next()
367fd4e5da5Sopenharmony_ci		switch {
368fd4e5da5Sopenharmony_ci		case r == '%':
369fd4e5da5Sopenharmony_ci			l.restore(s)
370fd4e5da5Sopenharmony_ci			l.pident()
371fd4e5da5Sopenharmony_ci		case r == '+' || r == '-' || r == '_' || isAlphaNumeric(r):
372fd4e5da5Sopenharmony_ci			l.restore(s)
373fd4e5da5Sopenharmony_ci			l.numberOrIdent()
374fd4e5da5Sopenharmony_ci		case r == '"':
375fd4e5da5Sopenharmony_ci			l.restore(s)
376fd4e5da5Sopenharmony_ci			l.string()
377fd4e5da5Sopenharmony_ci		case r == '=', r == '|':
378fd4e5da5Sopenharmony_ci			l.restore(s)
379fd4e5da5Sopenharmony_ci			l.operator()
380fd4e5da5Sopenharmony_ci		case r == ';':
381fd4e5da5Sopenharmony_ci			l.restore(s)
382fd4e5da5Sopenharmony_ci			l.lineComment()
383fd4e5da5Sopenharmony_ci		case r == '\n':
384fd4e5da5Sopenharmony_ci			l.restore(s)
385fd4e5da5Sopenharmony_ci			l.newline()
386fd4e5da5Sopenharmony_ci		}
387fd4e5da5Sopenharmony_ci	}
388fd4e5da5Sopenharmony_ci	if l.e != nil && l.e != io.EOF {
389fd4e5da5Sopenharmony_ci		return nil, nil, l.e
390fd4e5da5Sopenharmony_ci	}
391fd4e5da5Sopenharmony_ci	return l.toks, l.diags, nil
392fd4e5da5Sopenharmony_ci}
393fd4e5da5Sopenharmony_ci
394fd4e5da5Sopenharmony_cifunc isNumeric(r rune) bool      { return unicode.IsDigit(r) }
395fd4e5da5Sopenharmony_cifunc isAlpha(r rune) bool        { return unicode.IsLetter(r) }
396fd4e5da5Sopenharmony_cifunc isAlphaNumeric(r rune) bool { return isAlpha(r) || isNumeric(r) }
397fd4e5da5Sopenharmony_ci
398fd4e5da5Sopenharmony_citype parser struct {
399fd4e5da5Sopenharmony_ci	lines          []string                    // all source lines
400fd4e5da5Sopenharmony_ci	toks           []*Token                    // all tokens
401fd4e5da5Sopenharmony_ci	diags          []Diagnostic                // parser emitted diagnostics
402fd4e5da5Sopenharmony_ci	idents         map[string]*Identifier      // identifiers by name
403fd4e5da5Sopenharmony_ci	mappings       map[*Token]interface{}      // tokens to semantic map
404fd4e5da5Sopenharmony_ci	extInstImports map[string]schema.OpcodeMap // extension imports by identifier
405fd4e5da5Sopenharmony_ci	insts          []*Instruction              // all instructions
406fd4e5da5Sopenharmony_ci}
407fd4e5da5Sopenharmony_ci
408fd4e5da5Sopenharmony_cifunc (p *parser) parse() error {
409fd4e5da5Sopenharmony_ci	for i := 0; i < len(p.toks); {
410fd4e5da5Sopenharmony_ci		if p.newline(i) || p.comment(i) {
411fd4e5da5Sopenharmony_ci			i++
412fd4e5da5Sopenharmony_ci			continue
413fd4e5da5Sopenharmony_ci		}
414fd4e5da5Sopenharmony_ci		if n := p.instruction(i); n > 0 {
415fd4e5da5Sopenharmony_ci			i += n
416fd4e5da5Sopenharmony_ci		} else {
417fd4e5da5Sopenharmony_ci			p.unexpected(i)
418fd4e5da5Sopenharmony_ci			i++
419fd4e5da5Sopenharmony_ci		}
420fd4e5da5Sopenharmony_ci	}
421fd4e5da5Sopenharmony_ci	return nil
422fd4e5da5Sopenharmony_ci}
423fd4e5da5Sopenharmony_ci
424fd4e5da5Sopenharmony_ci// instruction parses the instruction starting at the i'th token.
425fd4e5da5Sopenharmony_cifunc (p *parser) instruction(i int) (n int) {
426fd4e5da5Sopenharmony_ci	inst := &Instruction{}
427fd4e5da5Sopenharmony_ci
428fd4e5da5Sopenharmony_ci	switch {
429fd4e5da5Sopenharmony_ci	case p.opcode(i) != nil:
430fd4e5da5Sopenharmony_ci		inst.Opcode = p.opcode(i)
431fd4e5da5Sopenharmony_ci		inst.Tokens = []*Token{p.tok(i)}
432fd4e5da5Sopenharmony_ci		p.mappings[p.tok(i)] = inst
433fd4e5da5Sopenharmony_ci		n++
434fd4e5da5Sopenharmony_ci	case p.opcode(i+2) != nil: // try '%id' '='
435fd4e5da5Sopenharmony_ci		inst.Result, inst.Opcode = p.pident(i), p.opcode(i+2)
436fd4e5da5Sopenharmony_ci		if inst.Result == nil || p.operator(i+1) != "=" {
437fd4e5da5Sopenharmony_ci			return 0
438fd4e5da5Sopenharmony_ci		}
439fd4e5da5Sopenharmony_ci		n += 3
440fd4e5da5Sopenharmony_ci		inst.Tokens = []*Token{p.tok(i), p.tok(i + 1), p.tok(i + 2)}
441fd4e5da5Sopenharmony_ci		p.mappings[p.tok(i+2)] = inst
442fd4e5da5Sopenharmony_ci	default:
443fd4e5da5Sopenharmony_ci		return
444fd4e5da5Sopenharmony_ci	}
445fd4e5da5Sopenharmony_ci
446fd4e5da5Sopenharmony_ci	expectsResult := len(inst.Opcode.Operands) > 0 && IsResult(inst.Opcode.Operands[0].Kind)
447fd4e5da5Sopenharmony_ci	operands := inst.Opcode.Operands
448fd4e5da5Sopenharmony_ci	switch {
449fd4e5da5Sopenharmony_ci	case inst.Result != nil && !expectsResult:
450fd4e5da5Sopenharmony_ci		p.err(inst.Result, "'%s' does not have a result", inst.Opcode.Opname)
451fd4e5da5Sopenharmony_ci		return
452fd4e5da5Sopenharmony_ci	case inst.Result == nil && expectsResult:
453fd4e5da5Sopenharmony_ci		p.err(p.tok(i), "'%s' expects a result", inst.Opcode.Opname)
454fd4e5da5Sopenharmony_ci		return
455fd4e5da5Sopenharmony_ci	case inst.Result != nil && expectsResult:
456fd4e5da5Sopenharmony_ci		// Check the result is of the correct type
457fd4e5da5Sopenharmony_ci		o := inst.Opcode.Operands[0]
458fd4e5da5Sopenharmony_ci		p.operand(o.Name, o.Kind, i, false)
459fd4e5da5Sopenharmony_ci		operands = operands[1:]
460fd4e5da5Sopenharmony_ci		p.addIdentDef(inst.Result.Text(p.lines), inst, p.tok(i))
461fd4e5da5Sopenharmony_ci	}
462fd4e5da5Sopenharmony_ci
463fd4e5da5Sopenharmony_ci	processOperand := func(o schema.Operand) bool {
464fd4e5da5Sopenharmony_ci		if p.newline(i + n) {
465fd4e5da5Sopenharmony_ci			return false
466fd4e5da5Sopenharmony_ci		}
467fd4e5da5Sopenharmony_ci
468fd4e5da5Sopenharmony_ci		switch o.Quantifier {
469fd4e5da5Sopenharmony_ci		case schema.Once:
470fd4e5da5Sopenharmony_ci			if op, c := p.operand(o.Name, o.Kind, i+n, false); op != nil {
471fd4e5da5Sopenharmony_ci				inst.Tokens = append(inst.Tokens, op.Tokens...)
472fd4e5da5Sopenharmony_ci				n += c
473fd4e5da5Sopenharmony_ci			}
474fd4e5da5Sopenharmony_ci		case schema.ZeroOrOnce:
475fd4e5da5Sopenharmony_ci			if op, c := p.operand(o.Name, o.Kind, i+n, true); op != nil {
476fd4e5da5Sopenharmony_ci				inst.Tokens = append(inst.Tokens, op.Tokens...)
477fd4e5da5Sopenharmony_ci				n += c
478fd4e5da5Sopenharmony_ci			}
479fd4e5da5Sopenharmony_ci		case schema.ZeroOrMany:
480fd4e5da5Sopenharmony_ci			for !p.newline(i + n) {
481fd4e5da5Sopenharmony_ci				if op, c := p.operand(o.Name, o.Kind, i+n, true); op != nil {
482fd4e5da5Sopenharmony_ci					inst.Tokens = append(inst.Tokens, op.Tokens...)
483fd4e5da5Sopenharmony_ci					n += c
484fd4e5da5Sopenharmony_ci				} else {
485fd4e5da5Sopenharmony_ci					return false
486fd4e5da5Sopenharmony_ci				}
487fd4e5da5Sopenharmony_ci			}
488fd4e5da5Sopenharmony_ci		}
489fd4e5da5Sopenharmony_ci		return true
490fd4e5da5Sopenharmony_ci	}
491fd4e5da5Sopenharmony_ci
492fd4e5da5Sopenharmony_ci	for _, o := range operands {
493fd4e5da5Sopenharmony_ci		if !processOperand(o) {
494fd4e5da5Sopenharmony_ci			break
495fd4e5da5Sopenharmony_ci		}
496fd4e5da5Sopenharmony_ci
497fd4e5da5Sopenharmony_ci		if inst.Opcode == schema.OpExtInst && n == 4 {
498fd4e5da5Sopenharmony_ci			extImportTok, extNameTok := p.tok(i+n), p.tok(i+n+1)
499fd4e5da5Sopenharmony_ci			extImport := extImportTok.Text(p.lines)
500fd4e5da5Sopenharmony_ci			if extOpcodes, ok := p.extInstImports[extImport]; ok {
501fd4e5da5Sopenharmony_ci				extName := extNameTok.Text(p.lines)
502fd4e5da5Sopenharmony_ci				if extOpcode, ok := extOpcodes[extName]; ok {
503fd4e5da5Sopenharmony_ci					n += 2 // skip ext import, ext name
504fd4e5da5Sopenharmony_ci					for _, o := range extOpcode.Operands {
505fd4e5da5Sopenharmony_ci						if !processOperand(o) {
506fd4e5da5Sopenharmony_ci							break
507fd4e5da5Sopenharmony_ci						}
508fd4e5da5Sopenharmony_ci					}
509fd4e5da5Sopenharmony_ci				} else {
510fd4e5da5Sopenharmony_ci					p.err(extNameTok, "Unknown extension opcode '%s'", extName)
511fd4e5da5Sopenharmony_ci				}
512fd4e5da5Sopenharmony_ci			} else {
513fd4e5da5Sopenharmony_ci				p.err(extImportTok, "Expected identifier to OpExtInstImport")
514fd4e5da5Sopenharmony_ci			}
515fd4e5da5Sopenharmony_ci		}
516fd4e5da5Sopenharmony_ci	}
517fd4e5da5Sopenharmony_ci
518fd4e5da5Sopenharmony_ci	for _, t := range inst.Tokens {
519fd4e5da5Sopenharmony_ci		inst.Range.grow(t.Range)
520fd4e5da5Sopenharmony_ci	}
521fd4e5da5Sopenharmony_ci
522fd4e5da5Sopenharmony_ci	p.insts = append(p.insts, inst)
523fd4e5da5Sopenharmony_ci
524fd4e5da5Sopenharmony_ci	if inst.Opcode == schema.OpExtInstImport && len(inst.Tokens) >= 4 {
525fd4e5da5Sopenharmony_ci		// Instruction is a OpExtInstImport. Keep track of this.
526fd4e5da5Sopenharmony_ci		extTok := inst.Tokens[3]
527fd4e5da5Sopenharmony_ci		extName := strings.Trim(extTok.Text(p.lines), `"`)
528fd4e5da5Sopenharmony_ci		extOpcodes, ok := schema.ExtOpcodes[extName]
529fd4e5da5Sopenharmony_ci		if !ok {
530fd4e5da5Sopenharmony_ci			p.err(extTok, "Unknown extension '%s'", extName)
531fd4e5da5Sopenharmony_ci		}
532fd4e5da5Sopenharmony_ci		extImport := inst.Result.Text(p.lines)
533fd4e5da5Sopenharmony_ci		p.extInstImports[extImport] = extOpcodes
534fd4e5da5Sopenharmony_ci	}
535fd4e5da5Sopenharmony_ci
536fd4e5da5Sopenharmony_ci	return
537fd4e5da5Sopenharmony_ci}
538fd4e5da5Sopenharmony_ci
539fd4e5da5Sopenharmony_ci// operand parses the operand with the name n, kind k, starting at the i'th
540fd4e5da5Sopenharmony_ci// token.
541fd4e5da5Sopenharmony_cifunc (p *parser) operand(n string, k *schema.OperandKind, i int, optional bool) (*Operand, int) {
542fd4e5da5Sopenharmony_ci	tok := p.tok(i)
543fd4e5da5Sopenharmony_ci	if tok == nil {
544fd4e5da5Sopenharmony_ci		return nil, 0
545fd4e5da5Sopenharmony_ci	}
546fd4e5da5Sopenharmony_ci
547fd4e5da5Sopenharmony_ci	op := &Operand{
548fd4e5da5Sopenharmony_ci		Name:   n,
549fd4e5da5Sopenharmony_ci		Kind:   k,
550fd4e5da5Sopenharmony_ci		Tokens: []*Token{tok},
551fd4e5da5Sopenharmony_ci	}
552fd4e5da5Sopenharmony_ci	p.mappings[tok] = op
553fd4e5da5Sopenharmony_ci
554fd4e5da5Sopenharmony_ci	switch k.Category {
555fd4e5da5Sopenharmony_ci	case schema.OperandCategoryBitEnum, schema.OperandCategoryValueEnum:
556fd4e5da5Sopenharmony_ci		s := tok.Text(p.lines)
557fd4e5da5Sopenharmony_ci		for _, e := range k.Enumerants {
558fd4e5da5Sopenharmony_ci			if e.Enumerant == s {
559fd4e5da5Sopenharmony_ci				count := 1
560fd4e5da5Sopenharmony_ci				for _, param := range e.Parameters {
561fd4e5da5Sopenharmony_ci					p, c := p.operand(param.Name, param.Kind, i+count, false)
562fd4e5da5Sopenharmony_ci					if p != nil {
563fd4e5da5Sopenharmony_ci						op.Tokens = append(op.Tokens, p.Tokens...)
564fd4e5da5Sopenharmony_ci						op.Parameters = append(op.Parameters, p)
565fd4e5da5Sopenharmony_ci					}
566fd4e5da5Sopenharmony_ci					count += c
567fd4e5da5Sopenharmony_ci				}
568fd4e5da5Sopenharmony_ci
569fd4e5da5Sopenharmony_ci				// Handle bitfield '|' chains
570fd4e5da5Sopenharmony_ci				if p.tok(i+count).Text(p.lines) == "|" {
571fd4e5da5Sopenharmony_ci					count++ // '|'
572fd4e5da5Sopenharmony_ci					p, c := p.operand(n, k, i+count, false)
573fd4e5da5Sopenharmony_ci					if p != nil {
574fd4e5da5Sopenharmony_ci						op.Tokens = append(op.Tokens, p.Tokens...)
575fd4e5da5Sopenharmony_ci						op.Parameters = append(op.Parameters, p)
576fd4e5da5Sopenharmony_ci					}
577fd4e5da5Sopenharmony_ci					count += c
578fd4e5da5Sopenharmony_ci				}
579fd4e5da5Sopenharmony_ci
580fd4e5da5Sopenharmony_ci				return op, count
581fd4e5da5Sopenharmony_ci			}
582fd4e5da5Sopenharmony_ci		}
583fd4e5da5Sopenharmony_ci		if !optional {
584fd4e5da5Sopenharmony_ci			p.err(p.tok(i), "invalid operand value '%s'", s)
585fd4e5da5Sopenharmony_ci		}
586fd4e5da5Sopenharmony_ci
587fd4e5da5Sopenharmony_ci		return nil, 0
588fd4e5da5Sopenharmony_ci
589fd4e5da5Sopenharmony_ci	case schema.OperandCategoryID:
590fd4e5da5Sopenharmony_ci		id := p.pident(i)
591fd4e5da5Sopenharmony_ci		if id != nil {
592fd4e5da5Sopenharmony_ci			p.addIdentRef(p.tok(i))
593fd4e5da5Sopenharmony_ci			return op, 1
594fd4e5da5Sopenharmony_ci		}
595fd4e5da5Sopenharmony_ci		if !optional {
596fd4e5da5Sopenharmony_ci			p.err(p.tok(i), "operand requires id, got '%s'", tok.Text(p.lines))
597fd4e5da5Sopenharmony_ci		}
598fd4e5da5Sopenharmony_ci		return nil, 0
599fd4e5da5Sopenharmony_ci
600fd4e5da5Sopenharmony_ci	case schema.OperandCategoryLiteral:
601fd4e5da5Sopenharmony_ci		switch tok.Type {
602fd4e5da5Sopenharmony_ci		case String, Integer, Float, Ident:
603fd4e5da5Sopenharmony_ci			return op, 1
604fd4e5da5Sopenharmony_ci		}
605fd4e5da5Sopenharmony_ci		if !optional {
606fd4e5da5Sopenharmony_ci			p.err(p.tok(i), "operand requires literal, got '%s'", tok.Text(p.lines))
607fd4e5da5Sopenharmony_ci		}
608fd4e5da5Sopenharmony_ci		return nil, 0
609fd4e5da5Sopenharmony_ci
610fd4e5da5Sopenharmony_ci	case schema.OperandCategoryComposite:
611fd4e5da5Sopenharmony_ci		n := 1
612fd4e5da5Sopenharmony_ci		for _, b := range k.Bases {
613fd4e5da5Sopenharmony_ci			o, c := p.operand(b.Kind, b, i+n, optional)
614fd4e5da5Sopenharmony_ci			if o != nil {
615fd4e5da5Sopenharmony_ci				op.Tokens = append(op.Tokens, o.Tokens...)
616fd4e5da5Sopenharmony_ci			}
617fd4e5da5Sopenharmony_ci			n += c
618fd4e5da5Sopenharmony_ci		}
619fd4e5da5Sopenharmony_ci		return op, n
620fd4e5da5Sopenharmony_ci
621fd4e5da5Sopenharmony_ci	default:
622fd4e5da5Sopenharmony_ci		p.err(p.tok(i), "OperandKind '%s' has unexpected category '%s'", k.Kind, k.Category)
623fd4e5da5Sopenharmony_ci		return nil, 0
624fd4e5da5Sopenharmony_ci	}
625fd4e5da5Sopenharmony_ci}
626fd4e5da5Sopenharmony_ci
627fd4e5da5Sopenharmony_ci// tok returns the i'th token, or nil if i is out of bounds.
628fd4e5da5Sopenharmony_cifunc (p *parser) tok(i int) *Token {
629fd4e5da5Sopenharmony_ci	if i < 0 || i >= len(p.toks) {
630fd4e5da5Sopenharmony_ci		return nil
631fd4e5da5Sopenharmony_ci	}
632fd4e5da5Sopenharmony_ci	return p.toks[i]
633fd4e5da5Sopenharmony_ci}
634fd4e5da5Sopenharmony_ci
635fd4e5da5Sopenharmony_ci// opcode returns the schema.Opcode for the i'th token, or nil if the i'th token
636fd4e5da5Sopenharmony_ci// does not represent an opcode.
637fd4e5da5Sopenharmony_cifunc (p *parser) opcode(i int) *schema.Opcode {
638fd4e5da5Sopenharmony_ci	if tok := p.ident(i); tok != nil {
639fd4e5da5Sopenharmony_ci		name := tok.Text(p.lines)
640fd4e5da5Sopenharmony_ci		if inst, found := schema.Opcodes[name]; found {
641fd4e5da5Sopenharmony_ci			return inst
642fd4e5da5Sopenharmony_ci		}
643fd4e5da5Sopenharmony_ci	}
644fd4e5da5Sopenharmony_ci	return nil
645fd4e5da5Sopenharmony_ci}
646fd4e5da5Sopenharmony_ci
647fd4e5da5Sopenharmony_ci// operator returns the operator for the i'th token, or and empty string if the
648fd4e5da5Sopenharmony_ci// i'th token is not an operator.
649fd4e5da5Sopenharmony_cifunc (p *parser) operator(i int) string {
650fd4e5da5Sopenharmony_ci	if tok := p.tok(i); tok != nil && tok.Type == Operator {
651fd4e5da5Sopenharmony_ci		return tok.Text(p.lines)
652fd4e5da5Sopenharmony_ci	}
653fd4e5da5Sopenharmony_ci	return ""
654fd4e5da5Sopenharmony_ci}
655fd4e5da5Sopenharmony_ci
656fd4e5da5Sopenharmony_ci// ident returns the i'th token if it is an Ident, otherwise nil.
657fd4e5da5Sopenharmony_cifunc (p *parser) ident(i int) *Token {
658fd4e5da5Sopenharmony_ci	if tok := p.tok(i); tok != nil && tok.Type == Ident {
659fd4e5da5Sopenharmony_ci		return tok
660fd4e5da5Sopenharmony_ci	}
661fd4e5da5Sopenharmony_ci	return nil
662fd4e5da5Sopenharmony_ci}
663fd4e5da5Sopenharmony_ci
664fd4e5da5Sopenharmony_ci// pident returns the i'th token if it is an PIdent, otherwise nil.
665fd4e5da5Sopenharmony_cifunc (p *parser) pident(i int) *Token {
666fd4e5da5Sopenharmony_ci	if tok := p.tok(i); tok != nil && tok.Type == PIdent {
667fd4e5da5Sopenharmony_ci		return tok
668fd4e5da5Sopenharmony_ci	}
669fd4e5da5Sopenharmony_ci	return nil
670fd4e5da5Sopenharmony_ci}
671fd4e5da5Sopenharmony_ci
672fd4e5da5Sopenharmony_ci// comment returns true if the i'th token is a Comment, otherwise false.
673fd4e5da5Sopenharmony_cifunc (p *parser) comment(i int) bool {
674fd4e5da5Sopenharmony_ci	if tok := p.tok(i); tok != nil && tok.Type == Comment {
675fd4e5da5Sopenharmony_ci		return true
676fd4e5da5Sopenharmony_ci	}
677fd4e5da5Sopenharmony_ci	return false
678fd4e5da5Sopenharmony_ci}
679fd4e5da5Sopenharmony_ci
680fd4e5da5Sopenharmony_ci// newline returns true if the i'th token is a Newline, otherwise false.
681fd4e5da5Sopenharmony_cifunc (p *parser) newline(i int) bool {
682fd4e5da5Sopenharmony_ci	if tok := p.tok(i); tok != nil && tok.Type == Newline {
683fd4e5da5Sopenharmony_ci		return true
684fd4e5da5Sopenharmony_ci	}
685fd4e5da5Sopenharmony_ci	return false
686fd4e5da5Sopenharmony_ci}
687fd4e5da5Sopenharmony_ci
688fd4e5da5Sopenharmony_ci// unexpected emits an 'unexpected token error' for the i'th token.
689fd4e5da5Sopenharmony_cifunc (p *parser) unexpected(i int) {
690fd4e5da5Sopenharmony_ci	p.err(p.toks[i], "syntax error: unexpected '%s'", p.toks[i].Text(p.lines))
691fd4e5da5Sopenharmony_ci}
692fd4e5da5Sopenharmony_ci
693fd4e5da5Sopenharmony_ci// addIdentDef records the token definition for the instruction inst with the
694fd4e5da5Sopenharmony_ci// given id.
695fd4e5da5Sopenharmony_cifunc (p *parser) addIdentDef(id string, inst *Instruction, def *Token) {
696fd4e5da5Sopenharmony_ci	i, existing := p.idents[id]
697fd4e5da5Sopenharmony_ci	if !existing {
698fd4e5da5Sopenharmony_ci		i = &Identifier{}
699fd4e5da5Sopenharmony_ci		p.idents[id] = i
700fd4e5da5Sopenharmony_ci	}
701fd4e5da5Sopenharmony_ci	if i.Definition == nil {
702fd4e5da5Sopenharmony_ci		i.Definition = inst
703fd4e5da5Sopenharmony_ci	} else {
704fd4e5da5Sopenharmony_ci		p.err(def, "id '%v' redeclared", id)
705fd4e5da5Sopenharmony_ci	}
706fd4e5da5Sopenharmony_ci}
707fd4e5da5Sopenharmony_ci
708fd4e5da5Sopenharmony_ci// addIdentRef adds a identifier reference for the token ref.
709fd4e5da5Sopenharmony_cifunc (p *parser) addIdentRef(ref *Token) {
710fd4e5da5Sopenharmony_ci	id := ref.Text(p.lines)
711fd4e5da5Sopenharmony_ci	i, existing := p.idents[id]
712fd4e5da5Sopenharmony_ci	if !existing {
713fd4e5da5Sopenharmony_ci		i = &Identifier{}
714fd4e5da5Sopenharmony_ci		p.idents[id] = i
715fd4e5da5Sopenharmony_ci	}
716fd4e5da5Sopenharmony_ci	i.References = append(i.References, ref)
717fd4e5da5Sopenharmony_ci}
718fd4e5da5Sopenharmony_ci
719fd4e5da5Sopenharmony_ci// err appends an fmt.Printf style error into l.diags for the given token.
720fd4e5da5Sopenharmony_cifunc (p *parser) err(tok *Token, msg string, args ...interface{}) {
721fd4e5da5Sopenharmony_ci	rng := Range{}
722fd4e5da5Sopenharmony_ci	if tok != nil {
723fd4e5da5Sopenharmony_ci		rng = tok.Range
724fd4e5da5Sopenharmony_ci	}
725fd4e5da5Sopenharmony_ci	p.diags = append(p.diags, Diagnostic{
726fd4e5da5Sopenharmony_ci		Range:    rng,
727fd4e5da5Sopenharmony_ci		Severity: SeverityError,
728fd4e5da5Sopenharmony_ci		Message:  fmt.Sprintf(msg, args...),
729fd4e5da5Sopenharmony_ci	})
730fd4e5da5Sopenharmony_ci}
731fd4e5da5Sopenharmony_ci
732fd4e5da5Sopenharmony_ci// Parse parses the SPIR-V assembly string source, returning the parse results.
733fd4e5da5Sopenharmony_cifunc Parse(source string) (Results, error) {
734fd4e5da5Sopenharmony_ci	toks, diags, err := lex(source)
735fd4e5da5Sopenharmony_ci	if err != nil {
736fd4e5da5Sopenharmony_ci		return Results{}, err
737fd4e5da5Sopenharmony_ci	}
738fd4e5da5Sopenharmony_ci	lines := strings.SplitAfter(source, "\n")
739fd4e5da5Sopenharmony_ci	p := parser{
740fd4e5da5Sopenharmony_ci		lines:          lines,
741fd4e5da5Sopenharmony_ci		toks:           toks,
742fd4e5da5Sopenharmony_ci		idents:         map[string]*Identifier{},
743fd4e5da5Sopenharmony_ci		mappings:       map[*Token]interface{}{},
744fd4e5da5Sopenharmony_ci		extInstImports: map[string]schema.OpcodeMap{},
745fd4e5da5Sopenharmony_ci	}
746fd4e5da5Sopenharmony_ci	if err := p.parse(); err != nil {
747fd4e5da5Sopenharmony_ci		return Results{}, err
748fd4e5da5Sopenharmony_ci	}
749fd4e5da5Sopenharmony_ci	diags = append(diags, p.diags...)
750fd4e5da5Sopenharmony_ci	return Results{
751fd4e5da5Sopenharmony_ci		Lines:       lines,
752fd4e5da5Sopenharmony_ci		Tokens:      toks,
753fd4e5da5Sopenharmony_ci		Diagnostics: p.diags,
754fd4e5da5Sopenharmony_ci		Identifiers: p.idents,
755fd4e5da5Sopenharmony_ci		Mappings:    p.mappings,
756fd4e5da5Sopenharmony_ci	}, nil
757fd4e5da5Sopenharmony_ci}
758fd4e5da5Sopenharmony_ci
759fd4e5da5Sopenharmony_ci// IsResult returns true if k is used to store the result of an instruction.
760fd4e5da5Sopenharmony_cifunc IsResult(k *schema.OperandKind) bool {
761fd4e5da5Sopenharmony_ci	switch k {
762fd4e5da5Sopenharmony_ci	case schema.OperandKindIdResult, schema.OperandKindIdResultType:
763fd4e5da5Sopenharmony_ci		return true
764fd4e5da5Sopenharmony_ci	default:
765fd4e5da5Sopenharmony_ci		return false
766fd4e5da5Sopenharmony_ci	}
767fd4e5da5Sopenharmony_ci}
768fd4e5da5Sopenharmony_ci
769fd4e5da5Sopenharmony_ci// Results holds the output of Parse().
770fd4e5da5Sopenharmony_citype Results struct {
771fd4e5da5Sopenharmony_ci	Lines       []string
772fd4e5da5Sopenharmony_ci	Tokens      []*Token
773fd4e5da5Sopenharmony_ci	Diagnostics []Diagnostic
774fd4e5da5Sopenharmony_ci	Identifiers map[string]*Identifier // identifiers by name
775fd4e5da5Sopenharmony_ci	Mappings    map[*Token]interface{} // tokens to semantic map
776fd4e5da5Sopenharmony_ci}
777fd4e5da5Sopenharmony_ci
778fd4e5da5Sopenharmony_ci// Instruction describes a single instruction instance
779fd4e5da5Sopenharmony_citype Instruction struct {
780fd4e5da5Sopenharmony_ci	Tokens   []*Token       // all the tokens that make up the instruction
781fd4e5da5Sopenharmony_ci	Result   *Token         // the token that represents the result of the instruction, or nil
782fd4e5da5Sopenharmony_ci	Operands []*Operand     // the operands of the instruction
783fd4e5da5Sopenharmony_ci	Range    Range          // the textual range of the instruction
784fd4e5da5Sopenharmony_ci	Opcode   *schema.Opcode // the opcode for the instruction
785fd4e5da5Sopenharmony_ci}
786fd4e5da5Sopenharmony_ci
787fd4e5da5Sopenharmony_ci// Operand describes a single operand instance
788fd4e5da5Sopenharmony_citype Operand struct {
789fd4e5da5Sopenharmony_ci	Name       string              // name of the operand
790fd4e5da5Sopenharmony_ci	Kind       *schema.OperandKind // kind of the operand
791fd4e5da5Sopenharmony_ci	Tokens     []*Token            // all the tokens that make up the operand
792fd4e5da5Sopenharmony_ci	Parameters []*Operand          // all the parameters for the operand
793fd4e5da5Sopenharmony_ci}
794fd4e5da5Sopenharmony_ci
795fd4e5da5Sopenharmony_ci// Identifier describes a single, unique SPIR-V identifier (i.e. %32)
796fd4e5da5Sopenharmony_citype Identifier struct {
797fd4e5da5Sopenharmony_ci	Definition *Instruction // where the identifier was defined
798fd4e5da5Sopenharmony_ci	References []*Token     // all the places the identifier was referenced
799fd4e5da5Sopenharmony_ci}
800fd4e5da5Sopenharmony_ci
801fd4e5da5Sopenharmony_ci// Severity is an enumerator of diagnostic severities
802fd4e5da5Sopenharmony_citype Severity int
803fd4e5da5Sopenharmony_ci
804fd4e5da5Sopenharmony_ci// Severity levels
805fd4e5da5Sopenharmony_ciconst (
806fd4e5da5Sopenharmony_ci	SeverityError Severity = iota
807fd4e5da5Sopenharmony_ci	SeverityWarning
808fd4e5da5Sopenharmony_ci	SeverityInformation
809fd4e5da5Sopenharmony_ci	SeverityHint
810fd4e5da5Sopenharmony_ci)
811fd4e5da5Sopenharmony_ci
812fd4e5da5Sopenharmony_ci// Diagnostic holds a single diagnostic message that was generated while
813fd4e5da5Sopenharmony_ci// parsing.
814fd4e5da5Sopenharmony_citype Diagnostic struct {
815fd4e5da5Sopenharmony_ci	Range    Range
816fd4e5da5Sopenharmony_ci	Severity Severity
817fd4e5da5Sopenharmony_ci	Message  string
818fd4e5da5Sopenharmony_ci}
819