[Lldb-commits] [lldb] Draft: [LLDB] Add scalar literal node (PR #147064)

Ilia Kuklin via lldb-commits lldb-commits at lists.llvm.org
Fri Jul 4 07:24:50 PDT 2025


https://github.com/kuilpd created https://github.com/llvm/llvm-project/pull/147064

A draft to discuss scalar literal node implementation

>From cf1f908360399ac51770d9fb7e1dac03eceab0e9 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Thu, 3 Jul 2025 19:34:17 +0500
Subject: [PATCH] [LLDB] *WIP* Add scalar literal node and binary addition

---
 lldb/include/lldb/ValueObject/DILAST.h    |  58 ++++
 lldb/include/lldb/ValueObject/DILEval.h   |  15 +
 lldb/include/lldb/ValueObject/DILLexer.h  |   1 +
 lldb/include/lldb/ValueObject/DILParser.h |   3 +
 lldb/source/ValueObject/DILAST.cpp        |  21 ++
 lldb/source/ValueObject/DILEval.cpp       | 405 ++++++++++++++++++++++
 lldb/source/ValueObject/DILLexer.cpp      |  10 +-
 lldb/source/ValueObject/DILParser.cpp     |  74 +++-
 8 files changed, 581 insertions(+), 6 deletions(-)

diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index 709f0639135f1..a9d244031d55f 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -9,6 +9,7 @@
 #ifndef LLDB_VALUEOBJECT_DILAST_H
 #define LLDB_VALUEOBJECT_DILAST_H
 
+#include "lldb/ValueObject/DILLexer.h"
 #include "lldb/ValueObject/ValueObject.h"
 #include "llvm/Support/Error.h"
 #include <cstdint>
@@ -19,10 +20,12 @@ namespace lldb_private::dil {
 /// The various types DIL AST nodes (used by the DIL parser).
 enum class NodeKind {
   eArraySubscriptNode,
+  eBinaryOpNode,
   eBitExtractionNode,
   eErrorNode,
   eIdentifierNode,
   eMemberOfNode,
+  eScalarLiteralNode,
   eUnaryOpNode,
 };
 
@@ -32,6 +35,14 @@ enum class UnaryOpKind {
   Deref,  // "*"
 };
 
+enum class BinaryOpKind {
+  Add, // "+"
+  Sub, // "-"
+};
+
+/// Translates DIL tokens to BinaryOpKind.
+BinaryOpKind GetBinaryOpKindFromToken(Token::Kind token_kind);
+
 /// Forward declaration, for use in DIL AST nodes. Definition is at the very
 /// end of this file.
 class Visitor;
@@ -178,6 +189,49 @@ class BitFieldExtractionNode : public ASTNode {
   int64_t m_last_index;
 };
 
+class ScalarLiteralNode : public ASTNode {
+public:
+  ScalarLiteralNode(uint32_t location, lldb::BasicType type, Scalar value)
+      : ASTNode(location, NodeKind::eScalarLiteralNode), m_type(type),
+        m_value(value) {}
+
+  llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
+
+  lldb::BasicType GetType() const { return m_type; }
+  Scalar GetValue() const & { return m_value; }
+
+  static bool classof(const ASTNode *node) {
+    return node->GetKind() == NodeKind::eScalarLiteralNode;
+  }
+
+private:
+  lldb::BasicType m_type;
+  Scalar m_value;
+};
+
+class BinaryOpNode : public ASTNode {
+public:
+  BinaryOpNode(uint32_t location, BinaryOpKind kind, ASTNodeUP lhs,
+               ASTNodeUP rhs)
+      : ASTNode(location, NodeKind::eBinaryOpNode), m_kind(kind),
+        m_lhs(std::move(lhs)), m_rhs(std::move(rhs)) {}
+
+  llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
+
+  BinaryOpKind GetKind() const { return m_kind; }
+  ASTNode *GetLHS() const { return m_lhs.get(); }
+  ASTNode *GetRHS() const { return m_rhs.get(); }
+
+  static bool classof(const ASTNode *node) {
+    return node->GetKind() == NodeKind::eBinaryOpNode;
+  }
+
+private:
+  BinaryOpKind m_kind;
+  ASTNodeUP m_lhs;
+  ASTNodeUP m_rhs;
+};
+
 /// This class contains one Visit method for each specialized type of
 /// DIL AST node. The Visit methods are used to dispatch a DIL AST node to
 /// the correct function in the DIL expression evaluator for evaluating that
@@ -195,6 +249,10 @@ class Visitor {
   Visit(const ArraySubscriptNode *node) = 0;
   virtual llvm::Expected<lldb::ValueObjectSP>
   Visit(const BitFieldExtractionNode *node) = 0;
+  virtual llvm::Expected<lldb::ValueObjectSP>
+  Visit(const ScalarLiteralNode *node) = 0;
+  virtual llvm::Expected<lldb::ValueObjectSP>
+  Visit(const BinaryOpNode *node) = 0;
 };
 
 } // namespace lldb_private::dil
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index 45e29b3ddcd7b..9e9028f6122fd 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -54,6 +54,21 @@ class Interpreter : Visitor {
   Visit(const ArraySubscriptNode *node) override;
   llvm::Expected<lldb::ValueObjectSP>
   Visit(const BitFieldExtractionNode *node) override;
+  llvm::Expected<lldb::ValueObjectSP>
+  Visit(const ScalarLiteralNode *node) override;
+  llvm::Expected<lldb::ValueObjectSP> Visit(const BinaryOpNode *node) override;
+
+  lldb::ValueObjectSP
+  ConvertValueObjectToTypeSystem(lldb::ValueObjectSP valobj,
+                                 lldb::TypeSystemSP type_system);
+
+  llvm::Error PrepareBinaryAddition(lldb::ValueObjectSP &lhs,
+                                    lldb::ValueObjectSP &rhs,
+                                    uint32_t location);
+
+  llvm::Expected<lldb::ValueObjectSP>
+  EvaluateBinaryAddition(lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs,
+                         uint32_t location);
 
   // Used by the interpreter to create objects, perform casts, etc.
   lldb::TargetSP m_target;
diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h
index 9c1ba97680253..58059657bf3a5 100644
--- a/lldb/include/lldb/ValueObject/DILLexer.h
+++ b/lldb/include/lldb/ValueObject/DILLexer.h
@@ -34,6 +34,7 @@ class Token {
     minus,
     numeric_constant,
     period,
+    plus,
     r_paren,
     r_square,
     star,
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index 9eda7bac4a364..c57ef0cf28022 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -87,6 +87,7 @@ class DILParser {
   ASTNodeUP Run();
 
   ASTNodeUP ParseExpression();
+  ASTNodeUP ParseAdditiveExpression();
   ASTNodeUP ParseUnaryExpression();
   ASTNodeUP ParsePostfixExpression();
   ASTNodeUP ParsePrimaryExpression();
@@ -96,6 +97,8 @@ class DILParser {
   std::string ParseIdExpression();
   std::string ParseUnqualifiedId();
   std::optional<int64_t> ParseIntegerConstant();
+  ASTNodeUP ParseNumericLiteral();
+  ASTNodeUP ParseNumericConstant();
 
   void BailOut(const std::string &error, uint32_t loc, uint16_t err_len);
 
diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp
index b1cd824c2299e..7aee692f2b28a 100644
--- a/lldb/source/ValueObject/DILAST.cpp
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -11,6 +11,18 @@
 
 namespace lldb_private::dil {
 
+BinaryOpKind GetBinaryOpKindFromToken(Token::Kind token_kind) {
+  switch (token_kind) {
+  case Token::minus:
+    return BinaryOpKind::Sub;
+  case Token::plus:
+    return BinaryOpKind::Add;
+  default:
+    break;
+  }
+  llvm_unreachable("Unknown binary operator kind.");
+}
+
 llvm::Expected<lldb::ValueObjectSP> ErrorNode::Accept(Visitor *v) const {
   llvm_unreachable("Attempting to Visit a DIL ErrorNode.");
 }
@@ -37,4 +49,13 @@ BitFieldExtractionNode::Accept(Visitor *v) const {
   return v->Visit(this);
 }
 
+llvm::Expected<lldb::ValueObjectSP>
+ScalarLiteralNode::Accept(Visitor *v) const {
+  return v->Visit(this);
+}
+
+llvm::Expected<lldb::ValueObjectSP> BinaryOpNode::Accept(Visitor *v) const {
+  return v->Visit(this);
+}
+
 } // namespace lldb_private::dil
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 8ca9b4215985d..2e42b57e93042 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -7,7 +7,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "lldb/ValueObject/DILEval.h"
+#include "lldb/Core/Module.h"
 #include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/TypeSystem.h"
 #include "lldb/Symbol/VariableList.h"
 #include "lldb/Target/RegisterContext.h"
 #include "lldb/ValueObject/DILAST.h"
@@ -401,4 +403,407 @@ Interpreter::Visit(const BitFieldExtractionNode *node) {
   return child_valobj_sp;
 }
 
+static CompilerType GetBasicTypeFromCU(std::shared_ptr<StackFrame> ctx,
+                                       lldb::BasicType basic_type) {
+  SymbolContext symbol_context =
+      ctx->GetSymbolContext(lldb::eSymbolContextCompUnit);
+  auto language = symbol_context.comp_unit->GetLanguage();
+
+  symbol_context = ctx->GetSymbolContext(lldb::eSymbolContextModule);
+  auto type_system =
+      symbol_context.module_sp->GetTypeSystemForLanguage(language);
+
+  if (type_system)
+    if (auto compiler_type = type_system.get()->GetBasicTypeFromAST(basic_type))
+      return compiler_type;
+
+  CompilerType empty_type;
+  return empty_type;
+}
+
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::Visit(const ScalarLiteralNode *node) {
+  CompilerType result_type =
+      GetBasicTypeFromCU(m_exe_ctx_scope, node->GetType());
+  Scalar value = node->GetValue();
+
+  // Scalar later could be float or bool
+  if (result_type.IsInteger() || result_type.IsNullPtrType() ||
+      result_type.IsPointerType()) {
+    llvm::APInt val = value.GetAPSInt();
+    return ValueObject::CreateValueObjectFromAPInt(m_target, val, result_type,
+                                                   "result");
+  }
+
+  return lldb::ValueObjectSP();
+}
+
+static CompilerType GetBasicType(lldb::TypeSystemSP type_system,
+                                 lldb::BasicType basic_type) {
+  if (type_system)
+    if (auto compiler_type = type_system.get()->GetBasicTypeFromAST(basic_type))
+      return compiler_type;
+
+  CompilerType empty_type;
+  return empty_type;
+}
+
+static CompilerType DoIntegralPromotion(CompilerType from,
+                                        std::shared_ptr<StackFrame> ctx) {
+  if (!from.IsInteger() && !from.IsUnscopedEnumerationType())
+    return from;
+
+  if (!from.IsPromotableIntegerType())
+    return from;
+
+  if (from.IsUnscopedEnumerationType())
+    return DoIntegralPromotion(from.GetEnumerationIntegerType(), ctx);
+  lldb::BasicType builtin_type =
+      from.GetCanonicalType().GetBasicTypeEnumeration();
+  lldb::TypeSystemSP type_system = from.GetTypeSystem().GetSharedPointer();
+
+  uint64_t from_size = 0;
+  if (builtin_type == lldb::eBasicTypeWChar ||
+      builtin_type == lldb::eBasicTypeSignedWChar ||
+      builtin_type == lldb::eBasicTypeUnsignedWChar ||
+      builtin_type == lldb::eBasicTypeChar16 ||
+      builtin_type == lldb::eBasicTypeChar32) {
+    // Find the type that can hold the entire range of values for our type.
+    bool is_signed = from.IsSigned();
+    if (auto temp = from.GetByteSize(ctx.get()))
+      from_size = *temp;
+
+    CompilerType promote_types[] = {
+        GetBasicType(type_system, lldb::eBasicTypeInt),
+        GetBasicType(type_system, lldb::eBasicTypeUnsignedInt),
+        GetBasicType(type_system, lldb::eBasicTypeLong),
+        GetBasicType(type_system, lldb::eBasicTypeUnsignedLong),
+        GetBasicType(type_system, lldb::eBasicTypeLongLong),
+        GetBasicType(type_system, lldb::eBasicTypeUnsignedLongLong),
+    };
+    for (auto &type : promote_types) {
+      uint64_t byte_size = 0;
+      if (auto temp = type.GetByteSize(ctx.get()))
+        byte_size = *temp;
+      if (from_size < byte_size ||
+          (from_size == byte_size &&
+           is_signed == (bool)(type.GetTypeInfo() & lldb::eTypeIsSigned))) {
+        return type;
+      }
+    }
+
+    llvm_unreachable("char type should fit into long long");
+  }
+
+  // Here we can promote only to "int" or "unsigned int".
+  CompilerType int_type = GetBasicType(type_system, lldb::eBasicTypeInt);
+  uint64_t int_byte_size = 0;
+  if (auto temp = int_type.GetByteSize(ctx.get()))
+    int_byte_size = *temp;
+
+  // Signed integer types can be safely promoted to "int".
+  if (from.IsSigned()) {
+    return int_type;
+  }
+  // Unsigned integer types are promoted to "unsigned int" if "int" cannot hold
+  // their entire value range.
+  return (from_size == int_byte_size)
+             ? GetBasicType(type_system, lldb::eBasicTypeUnsignedInt)
+             : int_type;
+}
+
+static lldb::ValueObjectSP UnaryConversion(lldb::ValueObjectSP valobj,
+                                           std::shared_ptr<StackFrame> ctx) {
+  // Perform usual conversions for unary operators.
+  CompilerType in_type = valobj->GetCompilerType();
+  CompilerType result_type;
+
+  if (valobj->GetCompilerType().IsInteger() ||
+      valobj->GetCompilerType().IsUnscopedEnumerationType()) {
+    CompilerType promoted_type =
+        DoIntegralPromotion(valobj->GetCompilerType(), ctx);
+    if (!promoted_type.CompareTypes(valobj->GetCompilerType()))
+      return valobj->CastToBasicType(promoted_type);
+  }
+
+  return valobj;
+}
+
+static size_t ConversionRank(CompilerType type) {
+  // Get integer conversion rank
+  // https://eel.is/c++draft/conv.rank
+  switch (type.GetCanonicalType().GetBasicTypeEnumeration()) {
+  case lldb::eBasicTypeBool:
+    return 1;
+  case lldb::eBasicTypeChar:
+  case lldb::eBasicTypeSignedChar:
+  case lldb::eBasicTypeUnsignedChar:
+    return 2;
+  case lldb::eBasicTypeShort:
+  case lldb::eBasicTypeUnsignedShort:
+    return 3;
+  case lldb::eBasicTypeInt:
+  case lldb::eBasicTypeUnsignedInt:
+    return 4;
+  case lldb::eBasicTypeLong:
+  case lldb::eBasicTypeUnsignedLong:
+    return 5;
+  case lldb::eBasicTypeLongLong:
+  case lldb::eBasicTypeUnsignedLongLong:
+    return 6;
+
+    // The ranks of char16_t, char32_t, and wchar_t are equal to the
+    // ranks of their underlying types.
+  case lldb::eBasicTypeWChar:
+  case lldb::eBasicTypeSignedWChar:
+  case lldb::eBasicTypeUnsignedWChar:
+    return 3;
+  case lldb::eBasicTypeChar16:
+    return 3;
+  case lldb::eBasicTypeChar32:
+    return 4;
+
+  default:
+    break;
+  }
+  return 0;
+}
+
+static lldb::BasicType BasicTypeToUnsigned(lldb::BasicType basic_type) {
+  switch (basic_type) {
+  case lldb::eBasicTypeInt:
+    return lldb::eBasicTypeUnsignedInt;
+  case lldb::eBasicTypeLong:
+    return lldb::eBasicTypeUnsignedLong;
+  case lldb::eBasicTypeLongLong:
+    return lldb::eBasicTypeUnsignedLongLong;
+  default:
+    return basic_type;
+  }
+}
+
+static void PerformIntegerConversions(std::shared_ptr<StackFrame> ctx,
+                                      lldb::ValueObjectSP &lhs,
+                                      lldb::ValueObjectSP &rhs,
+                                      bool convert_lhs, bool convert_rhs) {
+  CompilerType l_type = lhs->GetCompilerType();
+  CompilerType r_type = rhs->GetCompilerType();
+  if (r_type.IsSigned() && !l_type.IsSigned()) {
+    uint64_t l_size = 0;
+    uint64_t r_size = 0;
+    if (auto temp = l_type.GetByteSize(ctx.get()))
+      l_size = *temp;
+    ;
+    if (auto temp = r_type.GetByteSize(ctx.get()))
+      r_size = *temp;
+    if (l_size <= r_size) {
+      if (r_size == l_size) {
+        lldb::TypeSystemSP type_system =
+            l_type.GetTypeSystem().GetSharedPointer();
+        auto r_type_unsigned = GetBasicType(
+            type_system,
+            BasicTypeToUnsigned(
+                r_type.GetCanonicalType().GetBasicTypeEnumeration()));
+        if (convert_rhs)
+          rhs = rhs->CastToBasicType(r_type_unsigned);
+      }
+    }
+  }
+  if (convert_lhs)
+    lhs = lhs->CastToBasicType(rhs->GetCompilerType());
+}
+
+static CompilerType ArithmeticConversions(lldb::ValueObjectSP &lhs,
+                                          lldb::ValueObjectSP &rhs,
+                                          std::shared_ptr<StackFrame> ctx) {
+  // Apply unary conversion (e.g. intergal promotion) for both operands.
+  lhs = UnaryConversion(lhs, ctx);
+  rhs = UnaryConversion(rhs, ctx);
+
+  CompilerType lhs_type = lhs->GetCompilerType();
+  CompilerType rhs_type = rhs->GetCompilerType();
+
+  if (lhs_type.CompareTypes(rhs_type))
+    return lhs_type;
+
+  // If either of the operands is not arithmetic (e.g. pointer), we're done.
+  if (!lhs_type.IsScalarType() || !rhs_type.IsScalarType()) {
+    CompilerType bad_type;
+    return bad_type;
+  }
+
+  // Removed floating point conversions
+  if (lhs_type.IsFloat() || rhs_type.IsFloat()) {
+    CompilerType bad_type;
+    return bad_type;
+  }
+
+  if (lhs_type.IsInteger() && rhs_type.IsInteger()) {
+    using Rank = std::tuple<size_t, bool>;
+    Rank l_rank = {ConversionRank(lhs_type), !lhs_type.IsSigned()};
+    Rank r_rank = {ConversionRank(rhs_type), !rhs_type.IsSigned()};
+
+    if (l_rank < r_rank) {
+      PerformIntegerConversions(ctx, lhs, rhs, true, true);
+    } else if (l_rank > r_rank) {
+      PerformIntegerConversions(ctx, rhs, lhs, true, true);
+    }
+  }
+
+  return rhs_type;
+}
+
+llvm::Error Interpreter::PrepareBinaryAddition(lldb::ValueObjectSP &lhs,
+                                               lldb::ValueObjectSP &rhs,
+                                               uint32_t location) {
+  // Operation '+' works for:
+  //
+  //  {scalar,unscoped_enum} <-> {scalar,unscoped_enum}
+  //  {integer,unscoped_enum} <-> pointer
+  //  pointer <-> {integer,unscoped_enum}
+  auto orig_lhs_type = lhs->GetCompilerType();
+  auto orig_rhs_type = rhs->GetCompilerType();
+  auto result_type = ArithmeticConversions(lhs, rhs, m_exe_ctx_scope);
+
+  if (result_type.IsScalarType())
+    return llvm::Error::success();
+
+  // Removed pointer arithmetics
+  return llvm::make_error<DILDiagnosticError>(m_expr, "unimplemented",
+                                              location);
+}
+
+static lldb::ValueObjectSP EvaluateArithmeticOpInteger(lldb::TargetSP target,
+                                                       BinaryOpKind kind,
+                                                       lldb::ValueObjectSP lhs,
+                                                       lldb::ValueObjectSP rhs,
+                                                       CompilerType rtype) {
+  assert(lhs->GetCompilerType().IsInteger() &&
+         rhs->GetCompilerType().IsInteger() &&
+         "invalid ast: both operands must be integers");
+
+  auto wrap = [target, rtype](auto value) {
+    return ValueObject::CreateValueObjectFromAPInt(target, value, rtype,
+                                                   "result");
+  };
+
+  llvm::Expected<llvm::APSInt> l_value = lhs->GetValueAsAPSInt();
+  llvm::Expected<llvm::APSInt> r_value = rhs->GetValueAsAPSInt();
+
+  if (l_value && r_value) {
+    llvm::APSInt l = *l_value;
+    llvm::APSInt r = *r_value;
+
+    switch (kind) {
+    case BinaryOpKind::Add:
+      return wrap(l + r);
+
+    default:
+      assert(false && "invalid ast: invalid arithmetic operation");
+      return lldb::ValueObjectSP();
+    }
+  } else {
+    return lldb::ValueObjectSP();
+  }
+}
+
+static lldb::ValueObjectSP EvaluateArithmeticOp(lldb::TargetSP target,
+                                                BinaryOpKind kind,
+                                                lldb::ValueObjectSP lhs,
+                                                lldb::ValueObjectSP rhs,
+                                                CompilerType rtype) {
+  assert((rtype.IsInteger() || rtype.IsFloat()) &&
+         "invalid ast: result type must either integer or floating point");
+
+  // Evaluate arithmetic operation for two integral values.
+  if (rtype.IsInteger()) {
+    return EvaluateArithmeticOpInteger(target, kind, lhs, rhs, rtype);
+  }
+
+  // Removed floating point arithmetics
+
+  return lldb::ValueObjectSP();
+}
+
+llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryAddition(
+    lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) {
+  // Addition of two arithmetic types.
+  if (lhs->GetCompilerType().IsScalarType() &&
+      rhs->GetCompilerType().IsScalarType()) {
+    return EvaluateArithmeticOp(m_target, BinaryOpKind::Add, lhs, rhs,
+                                lhs->GetCompilerType().GetCanonicalType());
+  }
+
+  // Removed pointer arithmetics
+  return llvm::make_error<DILDiagnosticError>(m_expr, "unimplemented",
+                                              location);
+}
+
+lldb::ValueObjectSP
+Interpreter::ConvertValueObjectToTypeSystem(lldb::ValueObjectSP valobj,
+                                            lldb::TypeSystemSP type_system) {
+  auto apsint = valobj->GetValueAsAPSInt();
+  if (apsint) {
+    llvm::APInt value = *apsint;
+    if (type_system) {
+      lldb::BasicType basic_type = valobj->GetCompilerType()
+                                       .GetCanonicalType()
+                                       .GetBasicTypeEnumeration();
+      if (auto compiler_type =
+              type_system.get()->GetBasicTypeFromAST(basic_type)) {
+        valobj->GetValue().SetCompilerType(compiler_type);
+        return ValueObject::CreateValueObjectFromAPInt(m_target, value,
+                                                       compiler_type, "result");
+      }
+    }
+  }
+
+  return lldb::ValueObjectSP();
+}
+
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::Visit(const BinaryOpNode *node) {
+  auto lhs_or_err = Evaluate(node->GetLHS());
+  if (!lhs_or_err)
+    return lhs_or_err;
+  lldb::ValueObjectSP lhs = *lhs_or_err;
+  auto rhs_or_err = Evaluate(node->GetRHS());
+  if (!rhs_or_err)
+    return rhs_or_err;
+  lldb::ValueObjectSP rhs = *rhs_or_err;
+
+  lldb::TypeSystemSP lhs_system =
+      lhs->GetCompilerType().GetTypeSystem().GetSharedPointer();
+  lldb::TypeSystemSP rhs_system =
+      rhs->GetCompilerType().GetTypeSystem().GetSharedPointer();
+
+  // Is this a correct way to check if type systems are the same?
+  if (lhs_system != rhs_system) {
+    // If one of the nodes is a scalar const, convert it to the
+    // type system of another one
+    if (node->GetLHS()->GetKind() == NodeKind::eScalarLiteralNode)
+      lhs = ConvertValueObjectToTypeSystem(lhs, rhs_system);
+    else if (node->GetRHS()->GetKind() == NodeKind::eScalarLiteralNode)
+      rhs = ConvertValueObjectToTypeSystem(rhs, lhs_system);
+    else
+      return llvm::make_error<DILDiagnosticError>(
+          m_expr, "incompatible type systems", node->GetLocation());
+  }
+
+  switch (node->GetKind()) {
+  case BinaryOpKind::Add:
+    if (auto err = PrepareBinaryAddition(lhs, rhs, node->GetLocation()))
+      return err;
+    return EvaluateBinaryAddition(lhs, rhs, node->GetLocation());
+
+    // Other ops
+
+  default:
+    break;
+  }
+
+  return llvm::make_error<DILDiagnosticError>(m_expr, "unimplemented",
+                                              node->GetLocation());
+}
+
 } // namespace lldb_private::dil
diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
index eaefaf484bc18..2031bf677d6d7 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -40,6 +40,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
     return "numeric_constant";
   case Kind::period:
     return "period";
+  case Kind::plus:
+    return "plus";
   case Kind::r_paren:
     return "r_paren";
   case Kind::r_square:
@@ -114,10 +116,10 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr,
     return Token(Token::identifier, maybe_word->str(), position);
 
   constexpr std::pair<Token::Kind, const char *> operators[] = {
-      {Token::amp, "&"},     {Token::arrow, "->"},   {Token::coloncolon, "::"},
-      {Token::l_paren, "("}, {Token::l_square, "["}, {Token::minus, "-"},
-      {Token::period, "."},  {Token::r_paren, ")"},  {Token::r_square, "]"},
-      {Token::star, "*"},
+      {Token::amp, "&"},      {Token::arrow, "->"},   {Token::coloncolon, "::"},
+      {Token::l_paren, "("},  {Token::l_square, "["}, {Token::minus, "-"},
+      {Token::period, "."},   {Token::plus, "+"},     {Token::r_paren, ")"},
+      {Token::r_square, "]"}, {Token::star, "*"},
   };
   for (auto [kind, str] : operators) {
     if (remainder.consume_front(str))
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index eac41fab90763..6de54eabc8ca1 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -80,9 +80,30 @@ ASTNodeUP DILParser::Run() {
 // Parse an expression.
 //
 //  expression:
-//    unary_expression
+//    additive_expression
 //
-ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); }
+ASTNodeUP DILParser::ParseExpression() { return ParseAdditiveExpression(); }
+
+// Parse an additive_expression.
+//
+//  additive_expression:
+//    unary_expression {"+" unary_expression}
+//    unary_expression {"-" unary_expression}
+//
+ASTNodeUP DILParser::ParseAdditiveExpression() {
+  auto lhs = ParseUnaryExpression();
+
+  while (CurToken().IsOneOf({Token::plus, Token::minus})) {
+    Token token = CurToken();
+    m_dil_lexer.Advance();
+    auto rhs = ParseUnaryExpression();
+    lhs = std::make_unique<BinaryOpNode>(
+        token.GetLocation(), GetBinaryOpKindFromToken(token.GetKind()),
+        std::move(lhs), std::move(rhs));
+  }
+
+  return lhs;
+}
 
 // Parse an unary_expression.
 //
@@ -183,6 +204,8 @@ ASTNodeUP DILParser::ParsePostfixExpression() {
 //    "(" expression ")"
 //
 ASTNodeUP DILParser::ParsePrimaryExpression() {
+  if (CurToken().Is(Token::numeric_constant))
+    return ParseNumericLiteral();
   if (CurToken().IsOneOf(
           {Token::coloncolon, Token::identifier, Token::l_paren})) {
     // Save the source location for the diagnostics message.
@@ -351,6 +374,7 @@ void DILParser::BailOut(const std::string &error, uint32_t loc,
 //  integer_literal:
 //    ? Integer constant ?
 //
+// Haven't removed this yet in order to not rewrite the array subscription
 std::optional<int64_t> DILParser::ParseIntegerConstant() {
   std::string number_spelling;
   if (CurToken().GetKind() == Token::minus) {
@@ -370,6 +394,52 @@ std::optional<int64_t> DILParser::ParseIntegerConstant() {
   return std::nullopt;
 }
 
+// Parse a numeric_literal.
+//
+//  numeric_literal:
+//    ? Token::numeric_constant ?
+//
+ASTNodeUP DILParser::ParseNumericLiteral() {
+  Expect(Token::numeric_constant);
+  ASTNodeUP numeric_constant = ParseNumericConstant();
+  if (numeric_constant->GetKind() == NodeKind::eErrorNode) {
+    BailOut(llvm::formatv("Failed to parse token as numeric-constant: {0}",
+                          CurToken()),
+            CurToken().GetLocation(), CurToken().GetSpelling().length());
+    return std::make_unique<ErrorNode>();
+  }
+  m_dil_lexer.Advance();
+  return numeric_constant;
+}
+
+static constexpr std::pair<const char *, lldb::BasicType> type_suffixes[] = {
+    {"ull", lldb::eBasicTypeUnsignedLongLong},
+    {"ul", lldb::eBasicTypeUnsignedLong},
+    {"u", lldb::eBasicTypeUnsignedInt},
+    {"ll", lldb::eBasicTypeLongLong},
+    {"l", lldb::eBasicTypeLong},
+};
+
+ASTNodeUP DILParser::ParseNumericConstant() {
+  Token token = CurToken();
+  auto spelling = token.GetSpelling();
+  llvm::StringRef spelling_ref = spelling;
+  lldb::BasicType type = lldb::eBasicTypeInt;
+  for (auto [suffix, t] : type_suffixes) {
+    if (spelling_ref.consume_back_insensitive(suffix)) {
+      type = t;
+      break;
+    }
+  }
+  llvm::APInt raw_value;
+  if (!spelling_ref.getAsInteger(0, raw_value)) {
+    Scalar scalar_value(raw_value);
+    return std::make_unique<ScalarLiteralNode>(token.GetLocation(), type,
+                                               scalar_value);
+  }
+  return std::make_unique<ErrorNode>();
+}
+
 void DILParser::Expect(Token::Kind kind) {
   if (CurToken().IsNot(kind)) {
     BailOut(llvm::formatv("expected {0}, got: {1}", kind, CurToken()),



More information about the lldb-commits mailing list