LLDB mainline
DILLexer.cpp
Go to the documentation of this file.
1//===-- DILLexer.cpp ------------------------------------------------------===//
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// This implements the recursive descent parser for the Data Inspection
8// Language (DIL), and its helper functions, which will eventually underlie the
9// 'frame variable' command. The language that this parser recognizes is
10// described in lldb/docs/dil-expr-lang.ebnf
11//
12//===----------------------------------------------------------------------===//
13
15#include "lldb/Utility/Status.h"
17#include "llvm/ADT/StringSwitch.h"
18
19namespace lldb_private::dil {
20
21llvm::StringRef Token::GetTokenName(Kind kind) {
22 switch (kind) {
23 case Kind::amp:
24 return "amp";
25 case Kind::ampamp:
26 return "ampamp";
27 case Kind::arrow:
28 return "arrow";
29 case Kind::caret:
30 return "caret";
31 case Kind::colon:
32 return "colon";
34 return "coloncolon";
35 case Kind::equal:
36 return "equal";
37 case Kind::exclaim:
38 return "exclaim";
39 case Kind::eof:
40 return "eof";
42 return "equalequal";
44 return "exclaimequal";
46 return "float_constant";
47 case Kind::greater:
48 return "greater";
50 return "greaterequal";
52 return "greatergreater";
54 return "identifier";
56 return "integer_constant";
57 case Kind::kw_false:
58 return "false";
59 case Kind::kw_true:
60 return "true";
61 case Kind::l_paren:
62 return "l_paren";
63 case Kind::l_square:
64 return "l_square";
65 case Kind::less:
66 return "less";
67 case Kind::lessequal:
68 return "lessequal";
69 case Kind::lessless:
70 return "lessless";
71 case Kind::minus:
72 return "minus";
74 return "minusequal";
75 case Token::percent:
76 return "percent";
77 case Kind::period:
78 return "period";
79 case Kind::pipe:
80 return "pipe";
81 case Kind::pipepipe:
82 return "pipepipe";
83 case Kind::plus:
84 return "plus";
85 case Kind::plusequal:
86 return "plusequal";
87 case Kind::question:
88 return "question";
89 case Kind::r_paren:
90 return "r_paren";
91 case Kind::r_square:
92 return "r_square";
93 case Token::slash:
94 return "slash";
95 case Token::star:
96 return "star";
97 case Token::tilde:
98 return "tilde";
99 }
100 llvm_unreachable("Unknown token name");
101}
102
103static bool IsLetter(char c) {
104 return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
105}
106
107static bool IsDigit(char c) { return '0' <= c && c <= '9'; }
108
109// A word starts with a letter, underscore, or dollar sign, followed by
110// letters ('a'..'z','A'..'Z'), digits ('0'..'9'), and/or underscores.
111static std::optional<llvm::StringRef> IsWord(llvm::StringRef expr,
112 llvm::StringRef &remainder) {
113 // Find the longest prefix consisting of letters, digits, underscors and
114 // '$'. If it doesn't start with a digit, then it's a word.
115 llvm::StringRef candidate = remainder.take_while(
116 [](char c) { return IsDigit(c) || IsLetter(c) || c == '_' || c == '$'; });
117 if (candidate.empty() || IsDigit(candidate[0]))
118 return std::nullopt;
119 remainder = remainder.drop_front(candidate.size());
120 return candidate;
121}
122
123static bool IsNumberBodyChar(char ch) {
124 return IsDigit(ch) || IsLetter(ch) || ch == '.';
125}
126
127static std::optional<llvm::StringRef> IsNumber(llvm::StringRef &remainder,
128 bool &isFloat) {
129 llvm::StringRef tail = remainder;
130 llvm::StringRef body = tail.take_while(IsNumberBodyChar);
131 size_t dots = body.count('.');
132 if (dots > 1 || dots == body.size())
133 return std::nullopt;
134 if (IsDigit(body.front()) || (body[0] == '.' && IsDigit(body[1]))) {
135 isFloat = dots == 1;
136 tail = tail.drop_front(body.size());
137 bool isHex = body.contains_insensitive('x');
138 bool hasExp = !isHex && body.contains_insensitive('e');
139 bool hasHexExp = isHex && body.contains_insensitive('p');
140 if (hasExp || hasHexExp) {
141 isFloat = true; // This marks numbers like 0x1p1 and 1e1 as float
142 if (body.ends_with_insensitive("e") || body.ends_with_insensitive("p"))
143 if (tail.consume_front("+") || tail.consume_front("-"))
144 tail = tail.drop_while(IsNumberBodyChar);
145 }
146 size_t number_length = remainder.size() - tail.size();
147 llvm::StringRef number = remainder.take_front(number_length);
148 remainder = remainder.drop_front(number_length);
149 return number;
150 }
151 return std::nullopt;
152}
153
154static llvm::Error IsNotAllowedByMode(llvm::StringRef expr, Token token,
155 lldb::DILMode mode) {
156 switch (mode) {
158 if (!token.IsOneOf({Token::identifier, Token::period, Token::eof})) {
159 return llvm::make_error<DILDiagnosticError>(
160 expr, llvm::formatv("{0} is not allowed in DIL simple mode", token),
161 token.GetLocation());
162 }
163 break;
165 if (!token.IsOneOf({Token::identifier, Token::integer_constant,
166 Token::period, Token::arrow, Token::star, Token::amp,
167 Token::l_square, Token::r_square, Token::eof})) {
168 return llvm::make_error<DILDiagnosticError>(
169 expr, llvm::formatv("{0} is not allowed in DIL legacy mode", token),
170 token.GetLocation());
171 }
172 break;
174 break;
175 }
176 return llvm::Error::success();
177}
178
179llvm::Expected<DILLexer> DILLexer::Create(llvm::StringRef expr,
180 lldb::DILMode mode) {
181 std::vector<Token> tokens;
182 llvm::StringRef remainder = expr;
183 do {
184 if (llvm::Expected<Token> t = Lex(expr, remainder)) {
185 Token token = *t;
186 if (llvm::Error error = IsNotAllowedByMode(expr, token, mode))
187 return error;
188 tokens.push_back(std::move(token));
189 } else {
190 return t.takeError();
191 }
192 } while (tokens.back().GetKind() != Token::eof);
193 return DILLexer(expr, std::move(tokens));
194}
195
196llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr,
197 llvm::StringRef &remainder) {
198 // Skip over whitespace (spaces).
199 remainder = remainder.ltrim();
200 llvm::StringRef::iterator cur_pos = remainder.begin();
201
202 // Check to see if we've reached the end of our input string.
203 if (remainder.empty())
204 return Token(Token::eof, "", (uint32_t)expr.size());
205
206 uint32_t position = cur_pos - expr.begin();
207 bool isFloat = false;
208 std::optional<llvm::StringRef> maybe_number = IsNumber(remainder, isFloat);
209 if (maybe_number) {
210 auto kind = isFloat ? Token::float_constant : Token::integer_constant;
211 return Token(kind, maybe_number->str(), position);
212 }
213 std::optional<llvm::StringRef> maybe_word = IsWord(expr, remainder);
214 if (maybe_word) {
215 llvm::StringRef word = *maybe_word;
216 Token::Kind kind = llvm::StringSwitch<Token::Kind>(word)
217 .Case("false", Token::kw_false)
218 .Case("true", Token::kw_true)
219 .Default(Token::identifier);
220 return Token(kind, word.str(), position);
221 }
222
223 // IMPORTANT: If two or more tokens share the same prefix, the tokens need to
224 // be ordered longest-to-shortest in the list below. E.g. '::' must come
225 // before ':', and '+=' must come before '+'.
226 constexpr std::pair<Token::Kind, const char *> operators[] = {
227 {Token::ampamp, "&&"},
228 {Token::arrow, "->"},
229 {Token::coloncolon, "::"},
230 {Token::equalequal, "=="},
231 {Token::exclaimequal, "!="},
232 {Token::greaterequal, ">="},
233 {Token::greatergreater, ">>"},
234 {Token::lessequal, "<="},
235 {Token::lessless, "<<"},
236 {Token::minusequal, "-="},
237 {Token::pipepipe, "||"},
238 {Token::plusequal, "+="},
239 {Token::amp, "&"},
240 {Token::caret, "^"},
241 {Token::colon, ":"},
242 {Token::equal, "="},
243 {Token::exclaim, "!"},
244 {Token::greater, ">"},
245 {Token::l_paren, "("},
246 {Token::l_square, "["},
247 {Token::less, "<"},
248 {Token::minus, "-"},
249 {Token::percent, "%"},
250 {Token::period, "."},
251 {Token::pipe, "|"},
252 {Token::plus, "+"},
253 {Token::question, "?"},
254 {Token::r_paren, ")"},
255 {Token::r_square, "]"},
256 {Token::slash, "/"},
257 {Token::star, "*"},
258 {Token::tilde, "~"},
259 };
260 for (auto [kind, str] : operators) {
261 if (remainder.consume_front(str))
262 return Token(kind, str, position);
263 }
264
265 // Unrecognized character(s) in string; unable to lex it.
266 return llvm::make_error<DILDiagnosticError>(expr, "unrecognized token",
267 position);
268}
269
270} // namespace lldb_private::dil
static llvm::raw_ostream & error(Stream &strm)
static llvm::Expected< DILLexer > Create(llvm::StringRef expr, lldb::DILMode mode=lldb::eDILModeFull)
Lexes all the tokens in expr and calls the private constructor with the lexed tokens.
Definition DILLexer.cpp:179
DILLexer(llvm::StringRef dil_expr, std::vector< Token > lexed_tokens)
Definition DILLexer.h:137
static llvm::Expected< Token > Lex(llvm::StringRef expr, llvm::StringRef &remainder)
Definition DILLexer.cpp:196
Class defining the tokens generated by the DIL lexer and used by the DIL parser.
Definition DILLexer.h:25
static llvm::StringRef GetTokenName(Kind kind)
Definition DILLexer.cpp:21
uint32_t GetLocation() const
Definition DILLexer.h:83
bool IsOneOf(llvm::ArrayRef< Kind > kinds) const
Definition DILLexer.h:79
static std::optional< llvm::StringRef > IsWord(llvm::StringRef expr, llvm::StringRef &remainder)
Definition DILLexer.cpp:111
static bool IsNumberBodyChar(char ch)
Definition DILLexer.cpp:123
static llvm::Error IsNotAllowedByMode(llvm::StringRef expr, Token token, lldb::DILMode mode)
Definition DILLexer.cpp:154
static bool IsLetter(char c)
Definition DILLexer.cpp:103
static std::optional< llvm::StringRef > IsNumber(llvm::StringRef &remainder, bool &isFloat)
Definition DILLexer.cpp:127
static bool IsDigit(char c)
Definition DILLexer.cpp:107
DILMode
Data Inspection Language (DIL) evaluation modes.
@ eDILModeFull
Allowed: everything supported by DIL.
@ eDILModeLegacy
Allowed: identifiers, integers, operators: '.', '->', '*', '&', '[]'.
@ eDILModeSimple
Allowed: identifiers, operators: '.'.