Skip to content

Commit 491619a

Browse files
authored
[LLDB] Add array subscription and integer parsing to DIL (llvm#138551)
1 parent 4e186f2 commit 491619a

File tree

13 files changed

+348
-14
lines changed

13 files changed

+348
-14
lines changed

lldb/docs/dil-expr-lang.ebnf

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,19 @@
66
expression = unary_expression ;
77
88
unary_expression = unary_operator expression
9-
| primary_expression ;
9+
| postfix_expression ;
1010
1111
unary_operator = "*" | "&" ;
1212
13+
postfix_expression = primary_expression
14+
| postfix_expression "[" integer_literal "]";
15+
1316
primary_expression = id_expression
1417
| "(" expression ")";
1518
1619
id_expression = unqualified_id
1720
| qualified_id
18-
| register ;
21+
| register ;
1922
2023
unqualified_id = identifier ;
2124
@@ -24,6 +27,8 @@ qualified_id = ["::"] [nested_name_specifier] unqualified_id
2427
2528
identifier = ? C99 Identifier ? ;
2629
30+
integer_literal = ? Integer constant: hexademical, decimal, octal, binary ? ;
31+
2732
register = "$" ? Register name ? ;
2833
2934
nested_name_specifier = type_name "::"

lldb/include/lldb/ValueObject/DILAST.h

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ namespace lldb_private::dil {
1818

1919
/// The various types DIL AST nodes (used by the DIL parser).
2020
enum class NodeKind {
21+
eArraySubscriptNode,
2122
eErrorNode,
2223
eIdentifierNode,
24+
eScalarLiteralNode,
2325
eUnaryOpNode,
2426
};
2527

@@ -96,8 +98,8 @@ class UnaryOpNode : public ASTNode {
9698

9799
llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
98100

99-
UnaryOpKind kind() const { return m_kind; }
100-
ASTNode *operand() const { return m_operand.get(); }
101+
UnaryOpKind GetKind() const { return m_kind; }
102+
ASTNode *GetOperand() const { return m_operand.get(); }
101103

102104
static bool classof(const ASTNode *node) {
103105
return node->GetKind() == NodeKind::eUnaryOpNode;
@@ -108,6 +110,26 @@ class UnaryOpNode : public ASTNode {
108110
ASTNodeUP m_operand;
109111
};
110112

113+
class ArraySubscriptNode : public ASTNode {
114+
public:
115+
ArraySubscriptNode(uint32_t location, ASTNodeUP base, int64_t index)
116+
: ASTNode(location, NodeKind::eArraySubscriptNode),
117+
m_base(std::move(base)), m_index(index) {}
118+
119+
llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
120+
121+
ASTNode *GetBase() const { return m_base.get(); }
122+
int64_t GetIndex() const { return m_index; }
123+
124+
static bool classof(const ASTNode *node) {
125+
return node->GetKind() == NodeKind::eArraySubscriptNode;
126+
}
127+
128+
private:
129+
ASTNodeUP m_base;
130+
int64_t m_index;
131+
};
132+
111133
/// This class contains one Visit method for each specialized type of
112134
/// DIL AST node. The Visit methods are used to dispatch a DIL AST node to
113135
/// the correct function in the DIL expression evaluator for evaluating that
@@ -119,6 +141,8 @@ class Visitor {
119141
Visit(const IdentifierNode *node) = 0;
120142
virtual llvm::Expected<lldb::ValueObjectSP>
121143
Visit(const UnaryOpNode *node) = 0;
144+
virtual llvm::Expected<lldb::ValueObjectSP>
145+
Visit(const ArraySubscriptNode *node) = 0;
122146
};
123147

124148
} // namespace lldb_private::dil

lldb/include/lldb/ValueObject/DILEval.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ class Interpreter : Visitor {
5050
llvm::Expected<lldb::ValueObjectSP>
5151
Visit(const IdentifierNode *node) override;
5252
llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode *node) override;
53+
llvm::Expected<lldb::ValueObjectSP>
54+
Visit(const ArraySubscriptNode *node) override;
5355

5456
// Used by the interpreter to create objects, perform casts, etc.
5557
lldb::TargetSP m_target;

lldb/include/lldb/ValueObject/DILLexer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ class Token {
2929
eof,
3030
identifier,
3131
l_paren,
32+
l_square,
33+
numeric_constant,
3234
r_paren,
35+
r_square,
3336
star,
3437
};
3538

lldb/include/lldb/ValueObject/DILParser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,14 @@ class DILParser {
8484

8585
ASTNodeUP ParseExpression();
8686
ASTNodeUP ParseUnaryExpression();
87+
ASTNodeUP ParsePostfixExpression();
8788
ASTNodeUP ParsePrimaryExpression();
8889

8990
std::string ParseNestedNameSpecifier();
9091

9192
std::string ParseIdExpression();
9293
std::string ParseUnqualifiedId();
94+
std::optional<int64_t> ParseIntegerConstant();
9395

9496
void BailOut(const std::string &error, uint32_t loc, uint16_t err_len);
9597

lldb/source/ValueObject/DILAST.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,9 @@ llvm::Expected<lldb::ValueObjectSP> UnaryOpNode::Accept(Visitor *v) const {
2323
return v->Visit(this);
2424
}
2525

26+
llvm::Expected<lldb::ValueObjectSP>
27+
ArraySubscriptNode::Accept(Visitor *v) const {
28+
return v->Visit(this);
29+
}
30+
2631
} // namespace lldb_private::dil

lldb/source/ValueObject/DILEval.cpp

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,13 +237,13 @@ Interpreter::Visit(const IdentifierNode *node) {
237237
llvm::Expected<lldb::ValueObjectSP>
238238
Interpreter::Visit(const UnaryOpNode *node) {
239239
Status error;
240-
auto rhs_or_err = Evaluate(node->operand());
240+
auto rhs_or_err = Evaluate(node->GetOperand());
241241
if (!rhs_or_err)
242242
return rhs_or_err;
243243

244244
lldb::ValueObjectSP rhs = *rhs_or_err;
245245

246-
switch (node->kind()) {
246+
switch (node->GetKind()) {
247247
case UnaryOpKind::Deref: {
248248
lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic);
249249
if (dynamic_rhs)
@@ -272,4 +272,51 @@ Interpreter::Visit(const UnaryOpNode *node) {
272272
m_expr, "invalid ast: unexpected binary operator", node->GetLocation());
273273
}
274274

275+
llvm::Expected<lldb::ValueObjectSP>
276+
Interpreter::Visit(const ArraySubscriptNode *node) {
277+
auto lhs_or_err = Evaluate(node->GetBase());
278+
if (!lhs_or_err)
279+
return lhs_or_err;
280+
lldb::ValueObjectSP base = *lhs_or_err;
281+
282+
// Check to see if 'base' has a synthetic value; if so, try using that.
283+
uint64_t child_idx = node->GetIndex();
284+
if (lldb::ValueObjectSP synthetic = base->GetSyntheticValue()) {
285+
llvm::Expected<uint32_t> num_children =
286+
synthetic->GetNumChildren(child_idx + 1);
287+
if (!num_children)
288+
return llvm::make_error<DILDiagnosticError>(
289+
m_expr, toString(num_children.takeError()), node->GetLocation());
290+
if (child_idx >= *num_children) {
291+
std::string message = llvm::formatv(
292+
"array index {0} is not valid for \"({1}) {2}\"", child_idx,
293+
base->GetTypeName().AsCString("<invalid type>"),
294+
base->GetName().AsCString());
295+
return llvm::make_error<DILDiagnosticError>(m_expr, message,
296+
node->GetLocation());
297+
}
298+
if (lldb::ValueObjectSP child_valobj_sp =
299+
synthetic->GetChildAtIndex(child_idx))
300+
return child_valobj_sp;
301+
}
302+
303+
auto base_type = base->GetCompilerType().GetNonReferenceType();
304+
if (!base_type.IsPointerType() && !base_type.IsArrayType())
305+
return llvm::make_error<DILDiagnosticError>(
306+
m_expr, "subscripted value is not an array or pointer",
307+
node->GetLocation());
308+
if (base_type.IsPointerToVoid())
309+
return llvm::make_error<DILDiagnosticError>(
310+
m_expr, "subscript of pointer to incomplete type 'void'",
311+
node->GetLocation());
312+
313+
if (base_type.IsArrayType()) {
314+
if (lldb::ValueObjectSP child_valobj_sp = base->GetChildAtIndex(child_idx))
315+
return child_valobj_sp;
316+
}
317+
318+
int64_t signed_child_idx = node->GetIndex();
319+
return base->GetSyntheticArrayMember(signed_child_idx, true);
320+
}
321+
275322
} // namespace lldb_private::dil

lldb/source/ValueObject/DILLexer.cpp

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include "lldb/ValueObject/DILLexer.h"
1515
#include "lldb/Utility/Status.h"
16+
#include "lldb/ValueObject/DILParser.h"
1617
#include "llvm/ADT/StringSwitch.h"
1718

1819
namespace lldb_private::dil {
@@ -29,8 +30,14 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
2930
return "identifier";
3031
case Kind::l_paren:
3132
return "l_paren";
33+
case Kind::l_square:
34+
return "l_square";
35+
case Kind::numeric_constant:
36+
return "numeric_constant";
3237
case Kind::r_paren:
3338
return "r_paren";
39+
case Kind::r_square:
40+
return "r_square";
3441
case Token::star:
3542
return "star";
3643
}
@@ -57,6 +64,18 @@ static std::optional<llvm::StringRef> IsWord(llvm::StringRef expr,
5764
return candidate;
5865
}
5966

67+
static bool IsNumberBodyChar(char ch) { return IsDigit(ch) || IsLetter(ch); }
68+
69+
static std::optional<llvm::StringRef> IsNumber(llvm::StringRef expr,
70+
llvm::StringRef &remainder) {
71+
if (IsDigit(remainder[0])) {
72+
llvm::StringRef number = remainder.take_while(IsNumberBodyChar);
73+
remainder = remainder.drop_front(number.size());
74+
return number;
75+
}
76+
return std::nullopt;
77+
}
78+
6079
llvm::Expected<DILLexer> DILLexer::Create(llvm::StringRef expr) {
6180
std::vector<Token> tokens;
6281
llvm::StringRef remainder = expr;
@@ -81,21 +100,26 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr,
81100
return Token(Token::eof, "", (uint32_t)expr.size());
82101

83102
uint32_t position = cur_pos - expr.begin();
103+
std::optional<llvm::StringRef> maybe_number = IsNumber(expr, remainder);
104+
if (maybe_number)
105+
return Token(Token::numeric_constant, maybe_number->str(), position);
84106
std::optional<llvm::StringRef> maybe_word = IsWord(expr, remainder);
85107
if (maybe_word)
86108
return Token(Token::identifier, maybe_word->str(), position);
87109

88110
constexpr std::pair<Token::Kind, const char *> operators[] = {
89-
{Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("},
90-
{Token::r_paren, ")"}, {Token::star, "*"},
111+
{Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("},
112+
{Token::l_square, "["}, {Token::r_paren, ")"}, {Token::r_square, "]"},
113+
{Token::star, "*"},
91114
};
92115
for (auto [kind, str] : operators) {
93116
if (remainder.consume_front(str))
94117
return Token(kind, str, position);
95118
}
96119

97120
// Unrecognized character(s) in string; unable to lex it.
98-
return llvm::createStringError("Unable to lex input string");
121+
return llvm::make_error<DILDiagnosticError>(expr, "unrecognized token",
122+
position);
99123
}
100124

101125
} // namespace lldb_private::dil

lldb/source/ValueObject/DILParser.cpp

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,42 @@ ASTNodeUP DILParser::ParseUnaryExpression() {
111111
llvm_unreachable("invalid token kind");
112112
}
113113
}
114-
return ParsePrimaryExpression();
114+
return ParsePostfixExpression();
115+
}
116+
117+
// Parse a postfix_expression.
118+
//
119+
// postfix_expression:
120+
// primary_expression
121+
// postfix_expression "[" integer_literal "]"
122+
//
123+
ASTNodeUP DILParser::ParsePostfixExpression() {
124+
ASTNodeUP lhs = ParsePrimaryExpression();
125+
while (CurToken().Is(Token::l_square)) {
126+
uint32_t loc = CurToken().GetLocation();
127+
Token token = CurToken();
128+
switch (token.GetKind()) {
129+
case Token::l_square: {
130+
m_dil_lexer.Advance();
131+
std::optional<int64_t> rhs = ParseIntegerConstant();
132+
if (!rhs) {
133+
BailOut(
134+
llvm::formatv("failed to parse integer constant: {0}", CurToken()),
135+
CurToken().GetLocation(), CurToken().GetSpelling().length());
136+
return std::make_unique<ErrorNode>();
137+
}
138+
Expect(Token::r_square);
139+
m_dil_lexer.Advance();
140+
lhs = std::make_unique<ArraySubscriptNode>(loc, std::move(lhs),
141+
std::move(*rhs));
142+
break;
143+
}
144+
default:
145+
llvm_unreachable("invalid token");
146+
}
147+
}
148+
149+
return lhs;
115150
}
116151

117152
// Parse a primary_expression.
@@ -280,6 +315,23 @@ void DILParser::BailOut(const std::string &error, uint32_t loc,
280315
m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1);
281316
}
282317

318+
// Parse a integer_literal.
319+
//
320+
// integer_literal:
321+
// ? Integer constant ?
322+
//
323+
std::optional<int64_t> DILParser::ParseIntegerConstant() {
324+
auto spelling = CurToken().GetSpelling();
325+
llvm::StringRef spelling_ref = spelling;
326+
int64_t raw_value;
327+
if (!spelling_ref.getAsInteger<int64_t>(0, raw_value)) {
328+
m_dil_lexer.Advance();
329+
return raw_value;
330+
}
331+
332+
return std::nullopt;
333+
}
334+
283335
void DILParser::Expect(Token::Kind kind) {
284336
if (CurToken().IsNot(kind)) {
285337
BailOut(llvm::formatv("expected {0}, got: {1}", kind, CurToken()),
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CXX_SOURCES := main.cpp
2+
3+
include Makefile.rules

0 commit comments

Comments
 (0)