xref: /llvm-project/mlir/lib/AsmParser/LocationParser.cpp (revision 01e75646a5d4977a9e441e3db1042df0beccc4bb)
1 //===- LocationParser.cpp - MLIR Location Parser  -------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Parser.h"
10 #include "Token.h"
11 #include "mlir/IR/Attributes.h"
12 #include "mlir/IR/BuiltinAttributes.h"
13 #include "mlir/IR/Location.h"
14 #include "mlir/Support/LLVM.h"
15 #include <optional>
16 
17 using namespace mlir;
18 using namespace mlir::detail;
19 
20 /// Specific location instances.
21 ///
22 /// location-inst ::= filelinecol-location |
23 ///                   name-location |
24 ///                   callsite-location |
25 ///                   fused-location |
26 ///                   unknown-location
27 /// filelinecol-location ::= string-literal ':' integer-literal
28 ///                                         ':' integer-literal
29 /// name-location ::= string-literal
30 /// callsite-location ::= 'callsite' '(' location-inst 'at' location-inst ')'
31 /// fused-location ::= fused ('<' attribute-value '>')?
32 ///                    '[' location-inst (location-inst ',')* ']'
33 /// unknown-location ::= 'unknown'
34 ///
35 ParseResult Parser::parseCallSiteLocation(LocationAttr &loc) {
36   consumeToken(Token::bare_identifier);
37 
38   // Parse the '('.
39   if (parseToken(Token::l_paren, "expected '(' in callsite location"))
40     return failure();
41 
42   // Parse the callee location.
43   LocationAttr calleeLoc;
44   if (parseLocationInstance(calleeLoc))
45     return failure();
46 
47   // Parse the 'at'.
48   if (getToken().isNot(Token::bare_identifier) ||
49       getToken().getSpelling() != "at")
50     return emitWrongTokenError("expected 'at' in callsite location");
51   consumeToken(Token::bare_identifier);
52 
53   // Parse the caller location.
54   LocationAttr callerLoc;
55   if (parseLocationInstance(callerLoc))
56     return failure();
57 
58   // Parse the ')'.
59   if (parseToken(Token::r_paren, "expected ')' in callsite location"))
60     return failure();
61 
62   // Return the callsite location.
63   loc = CallSiteLoc::get(calleeLoc, callerLoc);
64   return success();
65 }
66 
67 ParseResult Parser::parseFusedLocation(LocationAttr &loc) {
68   consumeToken(Token::bare_identifier);
69 
70   // Try to parse the optional metadata.
71   Attribute metadata;
72   if (consumeIf(Token::less)) {
73     metadata = parseAttribute();
74     if (!metadata)
75       return failure();
76 
77     // Parse the '>' token.
78     if (parseToken(Token::greater,
79                    "expected '>' after fused location metadata"))
80       return failure();
81   }
82 
83   SmallVector<Location, 4> locations;
84   auto parseElt = [&] {
85     LocationAttr newLoc;
86     if (parseLocationInstance(newLoc))
87       return failure();
88     locations.push_back(newLoc);
89     return success();
90   };
91 
92   if (parseCommaSeparatedList(Delimiter::Square, parseElt,
93                               " in fused location"))
94     return failure();
95 
96   // Return the fused location.
97   loc = FusedLoc::get(locations, metadata, getContext());
98   return success();
99 }
100 
101 ParseResult Parser::parseNameOrFileLineColRange(LocationAttr &loc) {
102   auto *ctx = getContext();
103   auto str = getToken().getStringValue();
104   consumeToken(Token::string);
105 
106   std::optional<unsigned> startLine, startColumn, endLine, endColumn;
107 
108   // If the next token is ':' this is a filelinecol location.
109   if (consumeIf(Token::colon)) {
110     // Parse the line number.
111     if (getToken().isNot(Token::integer))
112       return emitWrongTokenError(
113           "expected integer line number in FileLineColRange");
114     startLine = getToken().getUnsignedIntegerValue();
115     if (!startLine)
116       return emitWrongTokenError(
117           "expected integer line number in FileLineColRange");
118     consumeToken(Token::integer);
119 
120     // Parse the ':'.
121     if (getToken().isNot(Token::colon)) {
122       loc = FileLineColRange::get(StringAttr::get(ctx, str), *startLine);
123       return success();
124     }
125     consumeToken(Token::colon);
126 
127     // Parse the column number.
128     if (getToken().isNot(Token::integer)) {
129       return emitWrongTokenError(
130           "expected integer column number in FileLineColRange");
131     }
132     startColumn = getToken().getUnsignedIntegerValue();
133     if (!startColumn.has_value())
134       return emitError("expected integer column number in FileLineColRange");
135     consumeToken(Token::integer);
136 
137     if (!isCurrentTokenAKeyword() || getTokenSpelling() != "to") {
138       loc = FileLineColLoc::get(ctx, str, *startLine, *startColumn);
139       return success();
140     }
141     consumeToken();
142 
143     // Parse the line number.
144     if (getToken().is(Token::integer)) {
145       endLine = getToken().getUnsignedIntegerValue();
146       if (!endLine) {
147         return emitWrongTokenError(
148             "expected integer line number in FileLineColRange");
149       }
150       consumeToken(Token::integer);
151     }
152 
153     // Parse the ':'.
154     if (getToken().isNot(Token::colon)) {
155       return emitWrongTokenError(
156           "expected either integer or `:` post `to` in FileLineColRange");
157     }
158     consumeToken(Token::colon);
159 
160     // Parse the column number.
161     if (getToken().isNot(Token::integer)) {
162       return emitWrongTokenError(
163           "expected integer column number in FileLineColRange");
164     }
165     endColumn = getToken().getUnsignedIntegerValue();
166     if (!endColumn.has_value())
167       return emitError("expected integer column number in FileLineColRange");
168     consumeToken(Token::integer);
169 
170     if (endLine.has_value()) {
171       loc = FileLineColRange::get(StringAttr::get(ctx, str), *startLine,
172                                   *startColumn, *endLine, *endColumn);
173     } else {
174       loc = FileLineColRange::get(StringAttr::get(ctx, str), *startLine,
175                                   *startColumn, *endColumn);
176     }
177     return success();
178   }
179 
180   // Otherwise, this is a NameLoc.
181 
182   // Check for a child location.
183   if (consumeIf(Token::l_paren)) {
184     // Parse the child location.
185     LocationAttr childLoc;
186     if (parseLocationInstance(childLoc))
187       return failure();
188 
189     loc = NameLoc::get(StringAttr::get(ctx, str), childLoc);
190 
191     // Parse the closing ')'.
192     if (parseToken(Token::r_paren,
193                    "expected ')' after child location of NameLoc"))
194       return failure();
195   } else {
196     loc = NameLoc::get(StringAttr::get(ctx, str));
197   }
198 
199   return success();
200 }
201 
202 ParseResult Parser::parseLocationInstance(LocationAttr &loc) {
203   // Handle aliases.
204   if (getToken().is(Token::hash_identifier)) {
205     Attribute locAttr = parseExtendedAttr(Type());
206     if (!locAttr)
207       return failure();
208     if (!(loc = dyn_cast<LocationAttr>(locAttr)))
209       return emitError("expected location attribute, but got") << locAttr;
210     return success();
211   }
212 
213   // Handle either name or filelinecol locations.
214   if (getToken().is(Token::string))
215     return parseNameOrFileLineColRange(loc);
216 
217   // Bare tokens required for other cases.
218   if (!getToken().is(Token::bare_identifier))
219     return emitWrongTokenError("expected location instance");
220 
221   // Check for the 'callsite' signifying a callsite location.
222   if (getToken().getSpelling() == "callsite")
223     return parseCallSiteLocation(loc);
224 
225   // If the token is 'fused', then this is a fused location.
226   if (getToken().getSpelling() == "fused")
227     return parseFusedLocation(loc);
228 
229   // Check for a 'unknown' for an unknown location.
230   if (getToken().getSpelling() == "unknown") {
231     consumeToken(Token::bare_identifier);
232     loc = UnknownLoc::get(getContext());
233     return success();
234   }
235 
236   return emitWrongTokenError("expected location instance");
237 }
238