嵌入式脚本语言中的对象模型设计与实现
对象模型的设计理念
在构建嵌入式脚本语言时,核心之一是设计一个灵活且高效的数据表示结构。本文描述的对象模型采用树形节点结构,支持四种基本类型:普通对象、指针、属性值和数组。这种设计允许通过路径表达式(如 user.profile[0].name)访问深层数据,适用于代码生成、配置解析等场景。
该模型的核心思想是将所有数据抽象为可嵌套的节点,每个节点根据其类型决定如何解释其内容。这使得系统能够统一处理复杂数据结构,同时保持接口简洁。
核心数据结构定义
使用 C++ 实现的主结构体 CNode 如下:
struct CNode {
enum NodeType { Object, Pointer, Value, Array };
NodeType type;
CNode* target; // 用于指针类型
std::string valueData; // 用于存储字符串或数字的值
std::map<std::string, CNode> children; // 子节点集合
std::vector<CNode> arrayItems; // 数组元素
CNode() : type(Object), target(nullptr) {}
// 各种操作方法见下文
};
该结构通过枚举区分不同类型,并仅在运行时依据 type 字段判断当前应使用哪个成员变量。若需优化内存占用,可改用 union 包裹非共享字段。
数据读取与写入封装
为避免直接暴露内部状态,提供了若干安全的访问方法:
std::string getValue() const {
switch (type) {
case Value:
return valueData;
case Object:
auto it = children.find("default");
if (it != children.end()) return it->second.getValue();
return "";
case Pointer:
return target ? target->getValue() : "";
case Array:
return "[array]";
default:
return "[unknown]";
}
}
void setValue(const std::string& val) {
type = Value;
valueData = val;
}
void setValue(int num) {
type = Value;
valueData = std::to_string(num);
}
此外提供向容器添加子项的方法:
void addObjectChild(const std::string& key, const CNode& node) {
type = Object;
children[key] = node;
}
void addReference(const std::string& key, CNode* ref) {
type = Object;
CNode ptrNode;
ptrNode.type = Pointer;
ptrNode.target = ref;
children[key] = ptrNode;
}
void createAndAppendToArray(const std::string& key) {
type = Object;
if (children.find(key) == children.end()) {
CNode arrNode;
arrNode.type = Array;
children[key] = arrNode;
}
}
void appendToArray(const std::string& key, const CNode& item) {
createAndAppendToArray(key);
children[key].arrayItems.push_back(item);
}
void appendValueToArray(const std::string& key, const std::string& val) {
CNode temp;
temp.setValue(val);
appendToArray(key, temp);
}
基于路径的对象查找机制
支持以点号分隔并包含数组索引的路径查询,例如 config.servers[2].ip。实现如下:
CNode* findNode(const std::string& path) {
std::vector<std::string> segments;
size_t start = 0;
while (start < path.size()) {
size_t end = path.find('.', start);
if (end == std::string::npos) end = path.size();
segments.push_back(path.substr(start, end - start));
start = end + 1;
}
CNode* current = this;
for (const auto& segment : segments) {
// 解析是否带索引:name[index]
size_t bracket = segment.find('[');
std::string name = segment;
int index = -1;
if (bracket != std::string::npos) {
name = segment.substr(0, bracket);
std::string idxStr = segment.substr(bracket + 1, segment.size() - bracket - 2);
index = std::stoi(idxStr);
}
// 跳转指针
while (current && current->type == Pointer) {
current = current->target;
}
if (!current) return nullptr;
if (current->type == Object) {
auto it = current->children.find(name);
if (it == current->children.end()) return nullptr;
current = &it->second;
} else {
return nullptr; // 非对象无法继续遍历
}
// 处理数组索引
if (index != -1) {
if (current->type != Array || index < 0 || index >= (int)current->arrayItems.size())
return nullptr;
current = ¤t->arrayItems[index];
}
}
return current;
}
结构化输出与调试显示
为了便于调试和日志输出,实现了带缩进的递归打印功能:
std::string toStringWithIndent(int depth = 0, int maxDepth = -1) const {
if (maxDepth >= 0 && depth >= maxDepth) {
return "\t" * depth + "[depth limit]\n";
}
std::string indent(depth * 4, ' ');
std::ostringstream oss;
switch (type) {
case Value:
oss << indent << "VALUE: \"" << valueData << "\"\n";
break;
case Pointer:
oss << indent << "-> POINTER:\n";
if (target) {
oss << target->toStringWithIndent(depth + 1, maxDepth);
} else {
oss << indent << " [null target]\n";
}
break;
case Array:
oss << indent << "ARRAY[" << arrayItems.size() << "]:\n";
for (size_t i = 0; i < arrayItems.size(); ++i) {
oss << indent << " [" << i << "]:\n";
oss << arrayItems[i].toStringWithIndent(depth + 2, maxDepth);
}
break;
case Object:
if (children.empty()) {
oss << indent << "{empty}\n";
} else {
oss << indent << "{\n";
for (const auto& pair : children) {
oss << indent << " " << pair.first << ":\n";
oss << pair.second.toStringWithIndent(depth + 2, maxDepth);
}
oss << indent << "}\n";
}
break;
default:
oss << indent << "[invalid type]\n";
break;
}
return oss.str();
}
此方法按层级缩进输出,支持深度限制,适合用于大型结构的概览展示。