(五) 动态模板语义分析-定义数据类型

上一节我们从模型上重建了AST节点,将antlr生成的表达式AST抽象成可直接运算的AST节点。
在运算之前我们必须首先定义出数据类型,定义出脚本支持的数据类型,这是运算的基础。
这一小节我们将定义出数据类型,在这里我们暂时定义四个数据类型:

enum Type { String, Number, Bool, Object, None };

完整的数据类型定义如下:

/// .h
typedef double number;

namespace DynamicDSL {
    enum Type { String, Number, Bool, Object, None };
    struct SemaASTNodeObject {
    private:
        any value;
    public:
        SemaASTNodeObject() {}
        SemaASTNodeObject(any value) {
            this->value = value;
        }
        ~SemaASTNodeObject() { }
    public:
        const Type getType();
        const string getTypeText();
        const any getRawValue() { return this->value; }
        void setValue(any v) { this->value = v; }
        /// 获取数据的字符串描述
        const string getText();
        template <typename T>
        T getValue() { return any_cast<T>(this->value); }
    public:
        bool isEmpty() { return !value.has_value(); }
        static SemaASTNodeObject empty() { return SemaASTNodeObject(); }
    };
}
/// .cpp
const DynamicDSL::Type DynamicDSL::SemaASTNodeObject::getType() {
    string clsName = this->value.type().name();
    if(clsName == "i" || clsName == "d"return Number;
    else if(clsName.find("string") != string::npos) return String;
    else if(clsName == "b"return Bool;
    else if(clsName.find("SemaASTNodeObject") != string::npos ||
            clsName.find("SemaContext") != string::npos)
        return Object;
    else if(clsName == "v"return None;
    else {
        return None;
    }
}
const string DynamicDSL::SemaASTNodeObject::getTypeText() {
    auto type = getType();
    switch (type) {
        case String:
            return "String";
            break;
        case Number:
            return "Number";
            break;
        case Bool:
            return "Bool";
            break;
        case Object:
            return "Object";
            break;
        default:
            break;
    }
    return "Unknow ASTNode Type";
}
const string DynamicDSL::SemaASTNodeObject::getText() {
    if(this->value.has_value()) {
        if(getType() == String) {
            return any_cast<string>(this->value);
        } else if(getType() == Number) {
            ostringstream oss;
            oss << any_cast<double>(this->value);
            return oss.str();
        } else if(getType() == Bool) {
            if(any_cast<bool>(this->value)) return "true";
            else return "false";
        } else if(getType() == Object) {
            string clsName = this->value.type().name();
            if(clsName.find("SemaASTNodeObject") != string::npos) {
                return getValue<SemaASTNodeObject>().getText();
            } else if(clsName.find("SemaContext") != string::npos) {
                return "[SemaContext Object]";
            } else {
                return "[Unknow Object]";
            }
        }
        return "[Unknow Value Type]";
    }
    return "[Empty Value]";
}

这里我们使用了any来包装我们具体的数据,所以我们的C++编译器需要设置成C++ 17
String类型我们使用的是std::string来实现。
Number类型则是上面提到的使用double来实现的。
Bool类型则是使用bool实现。
Object类型是单独定义的一个C++类型或者本身就是一个SemaASTNodeObject,后面单独说明。

通过std::any中的type()函数获取放置的数据信息来判断放置的数据的类型:

const DynamicDSL::Type DynamicDSL::SemaASTNodeObject::getType() {
    string clsName = this->value.type().name();
    if(clsName == "i" || clsName == "d"return Number;
    else if(clsName.find("string") != string::npos) return String;
    else if(clsName == "b"return Bool;
    else if(clsName.find("SemaASTNodeObject") != string::npos ||
            clsName.find("SemaContext") != string::npos)
        return Object;
    else if(clsName == "v"return None;
    else {
        return None;
    }
}

解包的时候判断再转换成具体的数据类即可,它的实现如下:

const string DynamicDSL::SemaASTNodeObject::getText() {
    if(this->value.has_value()) {
        if(getType() == String) {
            return any_cast<string>(this->value);
        } else if(getType() == Number) {
            ostringstream oss;
            oss << any_cast<double>(this->value);
            return oss.str();
        } else if(getType() == Bool) {
            if(any_cast<bool>(this->value)) return "true";
            else return "false";
        } else if(getType() == Object) {
            string clsName = this->value.type().name();
            if(clsName.find("SemaASTNodeObject") != string::npos) {
                return getValue<SemaASTNodeObject>().getText();
            } else if(clsName.find("SemaContext") != string::npos) {
                return "[SemaContext Object]";
            } else {
                return "[Unknow Object]";
            }
        }
        return "[Unknow Value Type]";
    }
    return "[Empty Value]";
}

为里需要特别说明一下Object这个类型中的另外一个自定义数据类型SemaContext。

它的完整定义如下:

namespace DynamicDSL {
    enum JSONType {
        JSONType_String,
        JSONType_Number,
        JSONType_Bool,
        JSONType_Object,
        JSONType_None
    };
}

namespace DynamicDSL {
    class SemaContext {
    private:
        /// 本地变量
        map<string, SemaASTNodeObject> variable;
        /// JSON注入
        Json json;
    public:
        SemaContext(Json json) {
            this->json = json;
        }
        SemaContext(Json json, map<string, SemaASTNodeObject> variable) {
            this->json = json;
            this->variable = variable;
        }
        static SemaContext *copy(SemaContext *context) {
            return new SemaContext(context->getJSON(), context->getVariable());
        }
    public:
        map<string, SemaASTNodeObject> getVariable() { return variable; }
        Json getJSON() { return json; }
        void putVariable(string key, SemaASTNodeObject val) {
            variable[key] = val;
        }
        SemaASTNodeObject getVariableValue(string key) {
            if(variable.count(key) > 0) return variable[key];
            DynamicDSL::SemaASTNodeObject object;
            if(!json[key].is_null()) {
                Json jsonValue = json[key];
                JSONType type = SemaContext::getValueType(jsonValue);
                if(type == DynamicDSL::JSONType_Bool) {
                    object.setValue(jsonValue.bool_value());
                } else if(type == DynamicDSL::JSONType_Number) {
                    object.setValue(jsonValue.number_value());
                } else if(type == DynamicDSL::JSONType_String) {
                    object.setValue(jsonValue.string_value());
                } else if(type == DynamicDSL::JSONType_Object) {
                    auto context = SemaContext(jsonValue);
                    object.setValue(context);
                }
            }
            return object;
        }
        static JSONType getValueType(Json jsonObject) {
            if(jsonObject.is_string()) return DynamicDSL::JSONType_String;
            else if(jsonObject.is_number()) return DynamicDSL::JSONType_Number;
            else if(jsonObject.is_bool()) return DynamicDSL::JSONType_Bool;
            else if(jsonObject.is_object()) return DynamicDSL::JSONType_Object;
            return DynamicDSL::JSONType_None;
        }
    };
};

这个类定义了栈帧执行时的变量上下文环境,由于特殊的使用场景,还多定义了一个注入的Json对象,这个Json对象在使用模板一开始的时候就注入了。

栈运行过程中产生的临时变量放入variable容器中,目前执行环境都是单独的一个表达式树,在没有对象与函数实现之前,这个执行环境比较单纯,这里只是提前考虑到执行环境这个问题,后期加入类与函数实现的时候可以更方便改造。

后面我们在消解变量的时候就需要从当前这个环境中查找对应的数据,找到后打包成一个SemaASTNodeObject对象,他的具体执行逻辑是这样的:

 SemaASTNodeObject getVariableValue(string key) {
            if(variable.count(key) > 0) return variable[key];
            DynamicDSL::SemaASTNodeObject object;
            if(!json[key].is_null()) {
                Json jsonValue = json[key];
                JSONType type = SemaContext::getValueType(jsonValue);
                if(type == DynamicDSL::JSONType_Bool) {
                    object.setValue(jsonValue.bool_value());
                } else if(type == DynamicDSL::JSONType_Number) {
                    object.setValue(jsonValue.number_value());
                } else if(type == DynamicDSL::JSONType_String) {
                    object.setValue(jsonValue.string_value());
                } else if(type == DynamicDSL::JSONType_Object) {
                    auto context = SemaContext(jsonValue);
                    object.setValue(context);
                }
            }
            return object;
        }

首先从临时变量中查找,如果找到了就直接返回,如果没找到就从注入的Json对象就查找。
到此,我们的类型系统就实现完成了, 我们还实现了栈帧执行的环境上下文。
下一节我们将在此基础上实现简最单纯的栈帧😂😂与AST节点的计算。

如果你觉得文章有用,请分享给更多的人阅读,谢谢!🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏


原文始发于微信公众号(程序猿搬砖):(五) 动态模板语义分析-定义数据类型

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/72013.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!