[Go to site: main page, start]

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 "float_constant";
44 return "greatergreater";
46 return "identifier";
48 return "integer_constant";
49 case Kind::kw_false:
50 return "false";
51 case Kind::kw_true:
52 return "true";
53 case Kind::l_paren:
54 return "l_paren";
55 case Kind::l_square:
56 return "l_square";
57 case Kind::lessless:
58 return "lessless";
59 case Kind::minus:
60 return "minus";
62 return "minusequal";
63 case Token::percent:
64 return "percent";
65 case Kind::period:
66 return "period";
67 case Kind::pipe:
68 return "pipe";
69 case Kind::pipepipe:
70 return "pipepipe";
71 case Kind::plus:
72 return "plus";
73 case Kind::plusequal:
74 return "plusequal";
75 case Kind::r_paren:
76 return "r_paren";
77 case Kind::r_square:
78 return "r_square";
79 case Token::slash:
80 return "slash";
81 case Token::star:
82 return "star";
83 case Token::tilde:
84 return "tilde";
85 }
86 llvm_unreachable("Unknown token name");
87}
88
89static bool IsLetter(char c) {
90 return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
91}
92
93static bool IsDigit(char c) { return '0' <= c && c <= '9'; }
94
95// A word starts with a letter, underscore, or dollar sign, followed by
96// letters ('a'..'z','A'..'Z'), digits ('0'..'9'), and/or underscores.
97static std::optional<llvm::StringRef> IsWord(llvm::StringRef expr,
98 llvm::StringRef &remainder) {
99 // Find the longest prefix consisting of letters, digits, underscors and
100 // '$'. If it doesn't start with a digit, then it's a word.
101 llvm::StringRef candidate = remainder.take_while(
102 [](char c) { return IsDigit(c) || IsLetter(c) || c == '_' || c == '$'; });
103 if (candidate.empty() || IsDigit(candidate[0]))
104 return std::nullopt;
105 remainder = remainder.drop_front(candidate.size());
106 return candidate;
107}
108
109static bool IsNumberBodyChar(char ch) {
110 return IsDigit(ch) || IsLetter(ch) || ch == '.';
111}
112
113static std::optional<llvm::StringRef> IsNumber(llvm::StringRef &remainder,
114 bool &isFloat) {
115 llvm::StringRef tail = remainder;
116 llvm::StringRef body = tail.take_while(IsNumberBodyChar);
117 size_t dots = body.count('.');
118 if (dots > 1 || dots == body.size())
119 return std::nullopt;
120 if (IsDigit(body.front()) || (body[0] == '.' && IsDigit(body[1]))) {
121 isFloat = dots == 1;
122 tail = tail.drop_front(body.size());
123 bool isHex = body.contains_insensitive('x');
124 bool hasExp = !isHex && body.contains_insensitive('e');
125 bool hasHexExp = isHex && body.contains_insensitive('p');
126 if (hasExp || hasHexExp) {
127 isFloat = true; // This marks numbers like 0x1p1 and 1e1 as float
128 if (body.ends_with_insensitive("e") || body.ends_with_insensitive("p"))
129 if (tail.consume_front("+") || tail.consume_front("-"))
130 tail = tail.drop_while(IsNumberBodyChar);
131 }
132 size_t number_length = remainder.size() - tail.size();
133 llvm::StringRef number = remainder.take_front(number_length);
134 remainder = remainder.drop_front(number_length);
135 return number;
136 }
137 return std::nullopt;
138}
139
140static llvm::Error IsNotAllowedByMode(llvm::StringRef expr, Token token,
141 lldb::DILMode mode) {
142 switch (mode) {
144 if (!token.IsOneOf({Token::identifier, Token::period, Token::eof})) {
145 return llvm::make_error<DILDiagnosticError>(
146 expr, llvm::formatv("{0} is not allowed in DIL simple mode", token),
147 token.GetLocation());
148 }
149 break;
151 if (!token.IsOneOf({Token::identifier, Token::integer_constant,
152 Token::period, Token::arrow, Token::star, Token::amp,
153 Token::l_square, Token::r_square, Token::eof})) {
154 return llvm::make_error<DILDiagnosticError>(
155 expr, llvm::formatv("{0} is not allowed in DIL legacy mode", token),
156 token.GetLocation());
157 }
158 break;
160 break;
161 }
162 return llvm::Error::success();
163}
164
165llvm::Expected<DILLexer> DILLexer::Create(llvm::StringRef expr,
166 lldb::DILMode mode) {
167 std::vector<Token> tokens;
168 llvm::StringRef remainder = expr;
169 do {
170 if (llvm::Expected<Token> t = Lex(expr, remainder)) {
171 Token token = *t;
172 if (llvm::Error error = IsNotAllowedByMode(expr, token, mode))
173 return error;
174 tokens.push_back(std::move(token));
175 } else {
176 return t.takeError();
177 }
178 } while (tokens.back().GetKind() != Token::eof);
179 return DILLexer(expr, std::move(tokens));
180}
181
182llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr,
183 llvm::StringRef &remainder) {
184 // Skip over whitespace (spaces).
185 remainder = remainder.ltrim();
186 llvm::StringRef::iterator cur_pos = remainder.begin();
187
188 // Check to see if we've reached the end of our input string.
189 if (remainder.empty())
190 return Token(Token::eof, "", (uint32_t)expr.size());
191
192 uint32_t position = cur_pos - expr.begin();
193 bool isFloat = false;
194 std::optional<llvm::StringRef> maybe_number = IsNumber(remainder, isFloat);
195 if (maybe_number) {
196 auto kind = isFloat ? Token::float_constant : Token::integer_constant;
197 return Token(kind, maybe_number->str(), position);
198 }
199 std::optional<llvm::StringRef> maybe_word = IsWord(expr, remainder);
200 if (maybe_word) {
201 llvm::StringRef word = *maybe_word;
202 Token::Kind kind = llvm::StringSwitch<Token::Kind>(word)
203 .Case("false", Token::kw_false)
204 .Case("true", Token::kw_true)
205 .Default(Token::identifier);
206 return Token(kind, word.str(), position);
207 }
208
209 // IMPORTANT: If two or more tokens share the same prefix, the tokens need to
210 // be ordered longest-to-shortest in the list below. E.g. '::' must come
211 // before ':', and '+=' must come before '+'.
212 constexpr std::pair<Token::Kind, const char *> operators[] = {
213 {Token::ampamp, "&&"}, {Token::arrow, "->"},
215 {Token::lessless, "<<"}, {Token::minusequal, "-="},
216 {Token::pipepipe, "||"}, {Token::plusequal, "+="},
217 {Token::amp, "&"}, {Token::caret, "^"},
218 {Token::colon, ":"}, {Token::equal, "="},
219 {Token::exclaim, "!"}, {Token::l_paren, "("},
220 {Token::l_square, "["}, {Token::minus, "-"},
221 {Token::percent, "%"}, {Token::period, "."},
222 {Token::pipe, "|"}, {Token::plus, "+"},
223 {Token::r_paren, ")"}, {Token::r_square, "]"},
224 {Token::slash, "/"}, {Token::star, "*"},
225 {Token::tilde, "~"},
226 };
227 for (auto [kind, str] : operators) {
228 if (remainder.consume_front(str))
229 return Token(kind, str, position);
230 }
231
232 // Unrecognized character(s) in string; unable to lex it.
233 return llvm::make_error<DILDiagnosticError>(expr, "unrecognized token",
234 position);
235}
236
237} // 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:165
DILLexer(llvm::StringRef dil_expr, std::vector< Token > lexed_tokens)
Definition DILLexer.h:130
static llvm::Expected< Token > Lex(llvm::StringRef expr, llvm::StringRef &remainder)
Definition DILLexer.cpp:182
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:76
bool IsOneOf(llvm::ArrayRef< Kind > kinds) const
Definition DILLexer.h:72
static std::optional< llvm::StringRef > IsWord(llvm::StringRef expr, llvm::StringRef &remainder)
Definition DILLexer.cpp:97
static bool IsNumberBodyChar(char ch)
Definition DILLexer.cpp:109
static llvm::Error IsNotAllowedByMode(llvm::StringRef expr, Token token, lldb::DILMode mode)
Definition DILLexer.cpp:140
static bool IsLetter(char c)
Definition DILLexer.cpp:89
static std::optional< llvm::StringRef > IsNumber(llvm::StringRef &remainder, bool &isFloat)
Definition DILLexer.cpp:113
static bool IsDigit(char c)
Definition DILLexer.cpp:93
DILMode
Data Inspection Language (DIL) evaluation modes.
@ eDILModeFull
Allowed: everything supported by DIL.
@ eDILModeLegacy
Allowed: identifiers, integers, operators: '.', '->', '*', '&', '[]'.
@ eDILModeSimple
Allowed: identifiers, operators: '.'.