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 
11 using namespace mlir;
12 using namespace mlir::detail;
13 
14 /// Specific location instances.
15 ///
16 /// location-inst ::= filelinecol-location |
17 ///                   name-location |
18 ///                   callsite-location |
19 ///                   fused-location |
20 ///                   unknown-location
21 /// filelinecol-location ::= string-literal ':' integer-literal
22 ///                                         ':' integer-literal
23 /// name-location ::= string-literal
24 /// callsite-location ::= 'callsite' '(' location-inst 'at' location-inst ')'
25 /// fused-location ::= fused ('<' attribute-value '>')?
26 ///                    '[' location-inst (location-inst ',')* ']'
27 /// unknown-location ::= 'unknown'
28 ///
parseCallSiteLocation(LocationAttr & loc)29 ParseResult Parser::parseCallSiteLocation(LocationAttr &loc) {
30   consumeToken(Token::bare_identifier);
31 
32   // Parse the '('.
33   if (parseToken(Token::l_paren, "expected '(' in callsite location"))
34     return failure();
35 
36   // Parse the callee location.
37   LocationAttr calleeLoc;
38   if (parseLocationInstance(calleeLoc))
39     return failure();
40 
41   // Parse the 'at'.
42   if (getToken().isNot(Token::bare_identifier) ||
43       getToken().getSpelling() != "at")
44     return emitError("expected 'at' in callsite location");
45   consumeToken(Token::bare_identifier);
46 
47   // Parse the caller location.
48   LocationAttr callerLoc;
49   if (parseLocationInstance(callerLoc))
50     return failure();
51 
52   // Parse the ')'.
53   if (parseToken(Token::r_paren, "expected ')' in callsite location"))
54     return failure();
55 
56   // Return the callsite location.
57   loc = CallSiteLoc::get(calleeLoc, callerLoc);
58   return success();
59 }
60 
parseFusedLocation(LocationAttr & loc)61 ParseResult Parser::parseFusedLocation(LocationAttr &loc) {
62   consumeToken(Token::bare_identifier);
63 
64   // Try to parse the optional metadata.
65   Attribute metadata;
66   if (consumeIf(Token::less)) {
67     metadata = parseAttribute();
68     if (!metadata)
69       return emitError("expected valid attribute metadata");
70     // Parse the '>' token.
71     if (parseToken(Token::greater,
72                    "expected '>' after fused location metadata"))
73       return failure();
74   }
75 
76   SmallVector<Location, 4> locations;
77   auto parseElt = [&] {
78     LocationAttr newLoc;
79     if (parseLocationInstance(newLoc))
80       return failure();
81     locations.push_back(newLoc);
82     return success();
83   };
84 
85   if (parseToken(Token::l_square, "expected '[' in fused location") ||
86       parseCommaSeparatedList(parseElt) ||
87       parseToken(Token::r_square, "expected ']' in fused location"))
88     return failure();
89 
90   // Return the fused location.
91   loc = FusedLoc::get(locations, metadata, getContext());
92   return success();
93 }
94 
parseNameOrFileLineColLocation(LocationAttr & loc)95 ParseResult Parser::parseNameOrFileLineColLocation(LocationAttr &loc) {
96   auto *ctx = getContext();
97   auto str = getToken().getStringValue();
98   consumeToken(Token::string);
99 
100   // If the next token is ':' this is a filelinecol location.
101   if (consumeIf(Token::colon)) {
102     // Parse the line number.
103     if (getToken().isNot(Token::integer))
104       return emitError("expected integer line number in FileLineColLoc");
105     auto line = getToken().getUnsignedIntegerValue();
106     if (!line.hasValue())
107       return emitError("expected integer line number in FileLineColLoc");
108     consumeToken(Token::integer);
109 
110     // Parse the ':'.
111     if (parseToken(Token::colon, "expected ':' in FileLineColLoc"))
112       return failure();
113 
114     // Parse the column number.
115     if (getToken().isNot(Token::integer))
116       return emitError("expected integer column number in FileLineColLoc");
117     auto column = getToken().getUnsignedIntegerValue();
118     if (!column.hasValue())
119       return emitError("expected integer column number in FileLineColLoc");
120     consumeToken(Token::integer);
121 
122     loc = FileLineColLoc::get(str, line.getValue(), column.getValue(), ctx);
123     return success();
124   }
125 
126   // Otherwise, this is a NameLoc.
127 
128   // Check for a child location.
129   if (consumeIf(Token::l_paren)) {
130     auto childSourceLoc = getToken().getLoc();
131 
132     // Parse the child location.
133     LocationAttr childLoc;
134     if (parseLocationInstance(childLoc))
135       return failure();
136 
137     // The child must not be another NameLoc.
138     if (childLoc.isa<NameLoc>())
139       return emitError(childSourceLoc,
140                        "child of NameLoc cannot be another NameLoc");
141     loc = NameLoc::get(Identifier::get(str, ctx), childLoc);
142 
143     // Parse the closing ')'.
144     if (parseToken(Token::r_paren,
145                    "expected ')' after child location of NameLoc"))
146       return failure();
147   } else {
148     loc = NameLoc::get(Identifier::get(str, ctx), ctx);
149   }
150 
151   return success();
152 }
153 
parseLocationInstance(LocationAttr & loc)154 ParseResult Parser::parseLocationInstance(LocationAttr &loc) {
155   // Handle either name or filelinecol locations.
156   if (getToken().is(Token::string))
157     return parseNameOrFileLineColLocation(loc);
158 
159   // Bare tokens required for other cases.
160   if (!getToken().is(Token::bare_identifier))
161     return emitError("expected location instance");
162 
163   // Check for the 'callsite' signifying a callsite location.
164   if (getToken().getSpelling() == "callsite")
165     return parseCallSiteLocation(loc);
166 
167   // If the token is 'fused', then this is a fused location.
168   if (getToken().getSpelling() == "fused")
169     return parseFusedLocation(loc);
170 
171   // Check for a 'unknown' for an unknown location.
172   if (getToken().getSpelling() == "unknown") {
173     consumeToken(Token::bare_identifier);
174     loc = UnknownLoc::get(getContext());
175     return success();
176   }
177 
178   return emitError("expected location instance");
179 }
180