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