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 (parseCommaSeparatedList(Delimiter::Square, parseElt,
86                               " in fused location"))
87     return failure();
88 
89   // Return the fused location.
90   loc = FusedLoc::get(locations, metadata, getContext());
91   return success();
92 }
93 
parseNameOrFileLineColLocation(LocationAttr & loc)94 ParseResult Parser::parseNameOrFileLineColLocation(LocationAttr &loc) {
95   auto *ctx = getContext();
96   auto str = getToken().getStringValue();
97   consumeToken(Token::string);
98 
99   // If the next token is ':' this is a filelinecol location.
100   if (consumeIf(Token::colon)) {
101     // Parse the line number.
102     if (getToken().isNot(Token::integer))
103       return emitError("expected integer line number in FileLineColLoc");
104     auto line = getToken().getUnsignedIntegerValue();
105     if (!line.hasValue())
106       return emitError("expected integer line number in FileLineColLoc");
107     consumeToken(Token::integer);
108 
109     // Parse the ':'.
110     if (parseToken(Token::colon, "expected ':' in FileLineColLoc"))
111       return failure();
112 
113     // Parse the column number.
114     if (getToken().isNot(Token::integer))
115       return emitError("expected integer column number in FileLineColLoc");
116     auto column = getToken().getUnsignedIntegerValue();
117     if (!column.hasValue())
118       return emitError("expected integer column number in FileLineColLoc");
119     consumeToken(Token::integer);
120 
121     loc = FileLineColLoc::get(ctx, str, line.getValue(), column.getValue());
122     return success();
123   }
124 
125   // Otherwise, this is a NameLoc.
126 
127   // Check for a child location.
128   if (consumeIf(Token::l_paren)) {
129     auto childSourceLoc = getToken().getLoc();
130 
131     // Parse the child location.
132     LocationAttr childLoc;
133     if (parseLocationInstance(childLoc))
134       return failure();
135 
136     // The child must not be another NameLoc.
137     if (childLoc.isa<NameLoc>())
138       return emitError(childSourceLoc,
139                        "child of NameLoc cannot be another NameLoc");
140     loc = NameLoc::get(Identifier::get(str, ctx), childLoc);
141 
142     // Parse the closing ')'.
143     if (parseToken(Token::r_paren,
144                    "expected ')' after child location of NameLoc"))
145       return failure();
146   } else {
147     loc = NameLoc::get(Identifier::get(str, ctx));
148   }
149 
150   return success();
151 }
152 
parseLocationInstance(LocationAttr & loc)153 ParseResult Parser::parseLocationInstance(LocationAttr &loc) {
154   // Handle either name or filelinecol locations.
155   if (getToken().is(Token::string))
156     return parseNameOrFileLineColLocation(loc);
157 
158   // Bare tokens required for other cases.
159   if (!getToken().is(Token::bare_identifier))
160     return emitError("expected location instance");
161 
162   // Check for the 'callsite' signifying a callsite location.
163   if (getToken().getSpelling() == "callsite")
164     return parseCallSiteLocation(loc);
165 
166   // If the token is 'fused', then this is a fused location.
167   if (getToken().getSpelling() == "fused")
168     return parseFusedLocation(loc);
169 
170   // Check for a 'unknown' for an unknown location.
171   if (getToken().getSpelling() == "unknown") {
172     consumeToken(Token::bare_identifier);
173     loc = UnknownLoc::get(getContext());
174     return success();
175   }
176 
177   return emitError("expected location instance");
178 }
179