From b4ee243b69a9685d17733daad8ed308d56d9212f Mon Sep 17 00:00:00 2001 From: chloro <13125187405@163.com> Date: Thu, 13 Jun 2024 17:40:01 +0800 Subject: [PATCH] add Enum Type --- TODO.md | 5 ++- include/wamon/ast.h | 17 ++++++++++ include/wamon/enum_def.h | 7 +++- include/wamon/inner_type_method.h | 2 +- include/wamon/interpreter.h | 2 +- include/wamon/package_unit.h | 11 ++++++ include/wamon/type.h | 5 ++- include/wamon/variable.h | 29 ++++++++++++++++ src/ast.cc | 8 ++++- src/parser.cc | 14 ++++++++ src/type.cc | 50 ++++++++++++++------------- src/type_checker.cc | 25 +++++++++++--- src/variable.cc | 56 ++++++++++++++++++++++++++++--- test/interpreter_test.cc | 38 +++++++++++++++++++++ 14 files changed, 231 insertions(+), 38 deletions(-) diff --git a/TODO.md b/TODO.md index f809ffa..1d5d989 100644 --- a/TODO.md +++ b/TODO.md @@ -20,7 +20,9 @@ * [done] 将interpreter的进入退出运行时栈接口改为私有的,不对用户暴露 * [done] lambda捕获列表支持move * [done] 内置/注册函数支持赋值给callable【目前不支持内置函数返回类型,因为有些内置函数是模板化的,其类型根据输入参数确定】 -* 支持enum +* [done] 支持enum + - enum的字面量格式应该是什么样的? + - [done] enum type:enum_item * [done] 优化内置方法的错误提示 * [done] 支持变量引用(不涉及类型系统) * [done] 支持自增运算符 @@ -37,3 +39,4 @@ * 返回类型为void时的return语句优化 * [done] 支持析构函数 * 支持内置函数assert +* 支持 enum to string \ No newline at end of file diff --git a/include/wamon/ast.h b/include/wamon/ast.h index 8003151..1b2e6d2 100644 --- a/include/wamon/ast.h +++ b/include/wamon/ast.h @@ -255,6 +255,23 @@ class VoidIteralExpr : public Expression { std::shared_ptr Calculate(Interpreter& interpreter) override { return std::make_shared(); } }; +class EnumIteralExpr : public Expression { + public: + std::shared_ptr Calculate(Interpreter& Interpreter) override; + + void SetEnumType(std::unique_ptr&& type) { type_ = std::move(type); } + + void SetEnumItem(const std::string& item) { enum_item_ = item; } + + const std::unique_ptr& GetEnumType() const { return type_; } + + const std::string& GetEnumItem() const { return enum_item_; } + + private: + std::unique_ptr type_; + std::string enum_item_; +}; + class SelfExpr : public Expression { public: std::shared_ptr Calculate(Interpreter& interpreter) override; diff --git a/include/wamon/enum_def.h b/include/wamon/enum_def.h index 1c97d69..ac90274 100644 --- a/include/wamon/enum_def.h +++ b/include/wamon/enum_def.h @@ -26,9 +26,14 @@ class EnumDef { const std::vector& GetEnumItems() const { return enum_items_; } + bool ItemExist(const std::string& item) const { + auto it = std::find(enum_items_.begin(), enum_items_.end(), item); + return it != enum_items_.end(); + } + private: std::string enum_name_; std::vector enum_items_; }; -} // namespace wamon \ No newline at end of file +} // namespace wamon diff --git a/include/wamon/inner_type_method.h b/include/wamon/inner_type_method.h index 70c53d7..ba25576 100644 --- a/include/wamon/inner_type_method.h +++ b/include/wamon/inner_type_method.h @@ -45,7 +45,7 @@ class InnerTypeMethod { } HandleType& Get(std::shared_ptr& obj, const std::string& method_name) const { - assert(!IsStructType(obj->GetType())); + assert(!IsStructOrEnumType(obj->GetType())); auto handle_id = GetHandleId(obj->GetType(), method_name); auto it = handles_.find(handle_id); assert(it != handles_.end()); diff --git a/include/wamon/interpreter.h b/include/wamon/interpreter.h index bd389bc..4690cc6 100644 --- a/include/wamon/interpreter.h +++ b/include/wamon/interpreter.h @@ -231,7 +231,7 @@ class Interpreter { // 注意:CallMethodByName会尝试进行struct trait -> struct的转换,而CallMethod不会,因此用户不要直接使用CallMethod接口 std::shared_ptr CallMethodByName(std::shared_ptr obj, const std::string& method_name, std::vector>&& params) { - if (!IsStructType(obj->GetType())) { + if (!IsStructOrEnumType(obj->GetType())) { return CallMethod(obj, method_name, std::move(params)); } auto type = obj->GetTypeInfo(); diff --git a/include/wamon/package_unit.h b/include/wamon/package_unit.h index dbfa0a9..d0eee96 100644 --- a/include/wamon/package_unit.h +++ b/include/wamon/package_unit.h @@ -72,6 +72,9 @@ class PackageUnit { void AddEnumDef(std::unique_ptr&& enum_def) { auto name = enum_def->GetEnumName(); + if (enum_def->GetEnumItems().empty()) { + throw WamonException("AddEnumDef error, empty enum {}", name); + } if (enums_.find(name) != enums_.end()) { throw WamonException("duplicate enum {}", name); } @@ -102,6 +105,14 @@ class PackageUnit { return it->second.get(); } + const EnumDef* FindEnum(const std::string& enum_name) const { + auto it = enums_.find(enum_name); + if (it == enums_.end()) { + return nullptr; + } + return it->second.get(); + } + const FunctionDef* FindFunction(const std::string& function_name) const { auto it = funcs_.find(function_name); if (it == funcs_.end()) { diff --git a/include/wamon/type.h b/include/wamon/type.h index 7742924..562b54f 100644 --- a/include/wamon/type.h +++ b/include/wamon/type.h @@ -22,6 +22,7 @@ * - bool * - void * - 结构体类型 + * - 枚举类型 * - 复合类型(类型加工器): * - 指针类型 * int* * - 列表类型 [num] int[2] @@ -180,7 +181,9 @@ inline bool IsBuiltInType(const std::unique_ptr& type) { type->GetTypeInfo() == "byte" || type->GetTypeInfo() == "bool" || type->GetTypeInfo() == "void"; } -inline bool IsStructType(const std::unique_ptr& type) { return type->IsBasicType() && !IsBuiltInType(type); } +inline bool IsStructOrEnumType(const std::unique_ptr& type) { + return type->IsBasicType() && !IsBuiltInType(type); +} namespace detail { diff --git a/include/wamon/variable.h b/include/wamon/variable.h index a35c448..6c0c30a 100644 --- a/include/wamon/variable.h +++ b/include/wamon/variable.h @@ -12,6 +12,7 @@ namespace wamon { class StructDef; +class EnumDef; /* * Variable为运行时变量类型的基类,每个Variable一定包含一个type成员标识其类型 @@ -446,6 +447,34 @@ inline StructVariable* AsStructVariable(const std::shared_ptr& v) { return static_cast(v.get()); } +class EnumVariable : public Variable { + public: + EnumVariable(const EnumDef* def, ValueCategory vc, const std::string& name); + + void ConstructByFields(const std::vector>& fields) override; + + void DefaultConstruct() override; + + // Clone函数返回值的 ValueCategory == RValue,name == "" + std::shared_ptr Clone() override; + + bool Compare(const std::shared_ptr& other) override; + + void Assign(const std::shared_ptr& other) override; + + nlohmann::json Print() override; + + void SetEnumItem(const std::string& enum_item) { enum_item_ = enum_item; } + + const std::string& GetEnumItem() const { return enum_item_; } + + private: + const EnumDef* def_; + std::string enum_item_; +}; + +inline EnumVariable* AsEnumVariable(const std::shared_ptr& v) { return static_cast(v.get()); } + class CompoundVariable : public Variable { public: CompoundVariable(std::unique_ptr&& t, ValueCategory vc, const std::string& name) diff --git a/src/ast.cc b/src/ast.cc index 7557a13..e346da7 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -54,7 +54,7 @@ std::shared_ptr MethodCallExpr::Calculate(Interpreter& interpreter) { params.push_back(std::move(v)); } auto v = caller_->Calculate(interpreter); - if (!IsStructType(v->GetType())) { + if (!IsStructOrEnumType(v->GetType())) { auto result = interpreter.CallMethod(v, method_name_, std::move(params)); return result; } @@ -122,6 +122,12 @@ std::shared_ptr IdExpr::Calculate(Interpreter& interpreter) { } } +std::shared_ptr EnumIteralExpr::Calculate(Interpreter& Interpreter) { + auto v = VariableFactory(type_, Variable::ValueCategory::RValue, "", Interpreter); + AsEnumVariable(v)->SetEnumItem(enum_item_); + return v; +} + std::shared_ptr SelfExpr::Calculate(Interpreter& interpreter) { return interpreter.GetSelfObject(); } std::shared_ptr LambdaExpr::Calculate(Interpreter& interpreter) { diff --git a/src/parser.cc b/src/parser.cc index ad284bf..e71abe5 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -416,6 +416,20 @@ std::unique_ptr ParseExpression(PackageUnit &pu, const std::vector enum_expr(new EnumIteralExpr()); + auto type = ParseType(pu, tokens, i); + AssertTokenOrThrow(tokens, i, Token::COLON, __FILE__, __LINE__); + auto enum_item = ParseIdentifier(pu, tokens, i); + enum_expr->SetEnumType(std::move(type)); + enum_expr->SetEnumItem(enum_item.second); + i -= 1; + operands.push(AttachUnaryOperators(std::move(enum_expr), u_operators)); + continue; + } if (current_token == Token::SELF) { std::unique_ptr self_expr(new SelfExpr()); operands.push(AttachUnaryOperators(std::move(self_expr), u_operators)); diff --git a/src/type.cc b/src/type.cc index 7462b7d..074adbd 100644 --- a/src/type.cc +++ b/src/type.cc @@ -96,7 +96,7 @@ void CheckCanConstructBy(const PackageUnit& pu, const std::unique_ptr& var if (param_types.size() == 1 && IsSameType(var_type, param_types[0])) { return; } - if (param_types.size() == 1 && IsStructType(param_types[0])) { + if (param_types.size() == 1 && IsStructOrEnumType(param_types[0])) { // 重载了()运算符的结构体类型的对象 auto method_name = OperatorDef::CreateName(Token::LEFT_PARENTHESIS, GetParamType(var_type)); auto struct_def = pu.FindStruct(param_types[0]->GetTypeInfo()); @@ -123,6 +123,7 @@ void CheckCanConstructBy(const PackageUnit& pu, const std::unique_ptr& var return; } // 所有相同类型的单个对象均可以执行构造(复制操作) + // also handle enum here if (param_types.size() == 1 && IsSameType(var_type, param_types[0])) { return; } @@ -143,36 +144,39 @@ void CheckCanConstructBy(const PackageUnit& pu, const std::unique_ptr& var return; } // struct 类型 - if (IsStructType(var_type)) { + if (IsStructOrEnumType(var_type)) { + // 这里不需要考虑Enum的情况,因为Enum的合理构造在 相同类型的单个对象构造中完成。 auto struct_def = pu.FindStruct(var_type->GetTypeInfo()); if (struct_def == nullptr) { throw WamonException("construct check error, invalid struct name {}", var_type->GetTypeInfo()); } - // struct trait - if (struct_def->IsTrait()) { - if (param_types.size() != 1) { - throw WamonException("struct trait {} construct check error, params should be only 1 but {}", - var_type->GetTypeInfo(), param_types.size()); + if (struct_def != nullptr) { + // struct trait + if (struct_def->IsTrait()) { + if (param_types.size() != 1) { + throw WamonException("struct trait {} construct check error, params should be only 1 but {}", + var_type->GetTypeInfo(), param_types.size()); + } + std::string reason; + if (CheckTraitConstraint(pu, var_type, param_types[0], reason) == false) { + throw WamonException("struct trait {} construct check error, trait constraint check failed, reason : {}", + var_type->GetTypeInfo(), reason); + } + return; } - std::string reason; - if (CheckTraitConstraint(pu, var_type, param_types[0], reason) == false) { - throw WamonException("struct trait {} construct check error, trait constraint check failed, reason : {}", - var_type->GetTypeInfo(), reason); + const auto& dms = struct_def->GetDataMembers(); + if (dms.size() != param_types.size()) { + throw WamonException("struct construct check error, {} != {}", dms.size(), param_types.size()); } - return; - } - const auto& dms = struct_def->GetDataMembers(); - if (dms.size() != param_types.size()) { - throw WamonException("struct construct check error, {} != {}", dms.size(), param_types.size()); - } - for (size_t i = 0; i < dms.size(); ++i) { - if (!IsSameType(dms[i].second, param_types[i])) { - throw WamonException( - "struct construct check error, {}th data member is constructd by the dismatch type, {} != {}", i, - dms[i].second->GetTypeInfo(), param_types[i]->GetTypeInfo()); + for (size_t i = 0; i < dms.size(); ++i) { + if (!IsSameType(dms[i].second, param_types[i])) { + throw WamonException( + "struct construct check error, {}th data member is constructd by the dismatch type, {} != {}", i, + dms[i].second->GetTypeInfo(), param_types[i]->GetTypeInfo()); + } } + return; } - return; } throw WamonException("CheckCanConstructBy check error, type {} can not be constructed by {} ", var_type->GetTypeInfo(), fmt::join(type_infos, ", ")); diff --git a/src/type_checker.cc b/src/type_checker.cc index 932638a..0488fef 100644 --- a/src/type_checker.cc +++ b/src/type_checker.cc @@ -93,8 +93,9 @@ void TypeChecker::CheckType(const std::unique_ptr& type, const std::string if (!IsBuiltInType(type)) { const PackageUnit& pu = static_analyzer_.GetPackageUnit(); const auto& struct_def = pu.FindStruct(type->GetTypeInfo()); - if (struct_def == nullptr) { - throw WamonTypeCheck(context_info, type->GetTypeInfo(), "invalid struct type"); + const auto& enum_def = pu.FindEnum(type->GetTypeInfo()); + if (struct_def == nullptr && enum_def == nullptr) { + throw WamonTypeCheck(context_info, type->GetTypeInfo(), "invalid struct type or enum type"); } } else { if (can_be_void == false && IsVoidType(type)) { @@ -268,6 +269,19 @@ std::unique_ptr CheckAndGetSSResultType(const TypeChecker& tc, BinaryExpr* return list_type->GetHoldType(); } +std::unique_ptr CheckAndGetEnumIteralResultType(const TypeChecker& tc, EnumIteralExpr* enum_expr) { + const auto& pu = tc.GetStaticAnalyzer().GetPackageUnit(); + auto enum_def = pu.FindEnum(enum_expr->GetEnumType()->GetTypeInfo()); + if (enum_def == nullptr) { + throw WamonException("invalid enum type : {}", enum_expr->GetEnumType()->GetTypeInfo()); + } + if (enum_def->ItemExist(enum_expr->GetEnumItem()) == false) { + throw WamonException("invalid enum item {} for enum type {}", enum_expr->GetEnumItem(), + enum_expr->GetEnumType()->GetTypeInfo()); + } + return enum_expr->GetEnumType()->Clone(); +} + std::unique_ptr CheckAndGetMemberAccessResultType(const TypeChecker& tc, BinaryExpr* binary_expr) { assert(binary_expr->op_ == Token::MEMBER_ACCESS); auto left_type = tc.GetExpressionType(binary_expr->left_.get()); @@ -498,7 +512,7 @@ std::unique_ptr CheckAndGetOperatorOverrideReturnType(const TypeChecker& t std::unique_ptr CheckAndGetInnerMethodReturnType(const TypeChecker& tc, const std::unique_ptr& ctype, const MethodCallExpr* call_expr) { - assert(!IsStructType(ctype)); + assert(!IsStructOrEnumType(ctype)); std::vector> params_type; for (auto& each : call_expr->parameters_) { params_type.push_back(tc.GetExpressionType(each.get())); @@ -582,7 +596,7 @@ std::unique_ptr CheckParamTypeAndGetResultTypeForFunction(const TypeChecke std::unique_ptr CheckParamTypeAndGetResultTypeForMethod(const TypeChecker& tc, MethodCallExpr* method_call_expr) { std::unique_ptr find_type = tc.GetExpressionType(method_call_expr->caller_.get()); - if (!IsStructType(find_type)) { + if (!IsStructOrEnumType(find_type)) { return CheckAndGetInnerMethodReturnType(tc, find_type, method_call_expr); } // if not find, throw exception @@ -657,6 +671,9 @@ std::unique_ptr TypeChecker::GetExpressionType(Expression* expr) const { if (dynamic_cast(expr)) { return std::make_unique(GetTokenStr(Token::VOID)); } + if (auto tmp = dynamic_cast(expr)) { + return CheckAndGetEnumIteralResultType(*this, tmp); + } if (auto tmp = dynamic_cast(expr)) { // 特殊的二元运算符,第二个操作数的类型由查询第一个运算符类型的定义得到 if (tmp->op_ == Token::MEMBER_ACCESS) { diff --git a/src/variable.cc b/src/variable.cc index 8356e3e..0830133 100644 --- a/src/variable.cc +++ b/src/variable.cc @@ -1,5 +1,6 @@ #include "wamon/variable.h" +#include "wamon/enum_def.h" #include "wamon/interpreter.h" #include "wamon/method_def.h" #include "wamon/struct_def.h" @@ -34,10 +35,15 @@ std::shared_ptr VariableFactory(const std::unique_ptr& type, Var throw WamonException("VariableFactory error, ip == nullptr"); } auto struct_def = ip->GetPackageUnit().FindStruct(type->GetTypeInfo()); - if (struct_def == nullptr) { - throw WamonException("pu.FindStruct error, type {} invalid", type->GetTypeInfo()); + if (struct_def != nullptr) { + return std::make_shared(struct_def, vc, *ip, name); } - return std::make_shared(struct_def, vc, *ip, name); + // enum + auto enum_def = ip->GetPackageUnit().FindEnum(type->GetTypeInfo()); + if (enum_def == nullptr) { + throw WamonException("VariableFactory error, invalid struct and enum type {}", type->GetTypeInfo()); + } + return std::make_shared(enum_def, vc, name); } if (IsPtrType(type)) { return std::make_unique(GetHoldType(type), vc, name); @@ -48,7 +54,7 @@ std::shared_ptr VariableFactory(const std::unique_ptr& type, Var if (IsFuncType(type)) { return std::make_unique(GetParamType(type), GetReturnType(type), vc, name); } - throw WamonException("VariableFactory error, not implement now."); + throw WamonException("VariableFactory error, invalid type {}", type->GetTypeInfo()); } std::shared_ptr VariableFactory(const std::unique_ptr& type, Variable::ValueCategory vc, @@ -283,6 +289,46 @@ nlohmann::json StructVariable::Print() { return obj; } +EnumVariable::EnumVariable(const EnumDef* def, ValueCategory vc, const std::string& name) + : Variable(std::make_unique(def->GetEnumName()), vc, name), def_(def) {} + +void EnumVariable::ConstructByFields(const std::vector>& fields) { + if (fields.size() != 1) { + throw WamonException("EnumVariable ConstructByFields error, param.size() == {}", fields.size()); + } + if (GetTypeInfo() != fields[0]->GetTypeInfo()) { + throw WamonException("EnumVariable ConstructByFields error, type mismatch : {} != {}", GetTypeInfo(), + fields[0]->GetTypeInfo()); + } + enum_item_ = static_cast(fields[0].get())->enum_item_; +} + +void EnumVariable::DefaultConstruct() { + assert(def_->GetEnumItems().empty() == false); + enum_item_ = def_->GetEnumItems()[0]; +} + +std::shared_ptr EnumVariable::Clone() { + auto tmp = std::make_shared(def_, Variable::ValueCategory::RValue, ""); + tmp->enum_item_ = enum_item_; + return tmp; +} + +bool EnumVariable::Compare(const std::shared_ptr& other) { + return enum_item_ == static_cast(other.get())->enum_item_; +} + +void EnumVariable::Assign(const std::shared_ptr& other) { + enum_item_ = static_cast(other.get())->enum_item_; +} + +nlohmann::json EnumVariable::Print() { + nlohmann::json j; + j["enum"] = def_->GetEnumName(); + j["enum_item"] = enum_item_; + return j; +} + void PointerVariable::ConstructByFields(const std::vector>& fields) { if (fields.size() != 1) { throw WamonException("PointerVariable's ConstructByFields method error : fields.size() == {}", fields.size()); @@ -353,7 +399,7 @@ void FunctionVariable::ConstructByFields(const std::vectorGetType())) { + if (IsStructOrEnumType(fields[0]->GetType())) { // structtype if (fields[0]->IsRValue()) { obj_ = std::move(fields[0]); diff --git a/test/interpreter_test.cc b/test/interpreter_test.cc index 586b5dd..7e1eede 100644 --- a/test/interpreter_test.cc +++ b/test/interpreter_test.cc @@ -1042,3 +1042,41 @@ TEST(interpreter, exec_expression) { ret = interpreter.ExecExpression(tc, "main", "v3 as int + v"); EXPECT_EQ(wamon::AsIntVariable(ret)->GetValue(), 15); } + +TEST(interpreter, enum) { + wamon::Scanner scan; + std::string script = R"( + package main; + + enum Color { + Red; + Yellow; + Blue; + } + + let v : Color = enum Color:Red; + + enum Color2 { + Red; + White; + Black; + } + )"; + wamon::PackageUnit pu; + auto tokens = scan.Scan(script); + pu = wamon::Parse(tokens); + pu = wamon::MergePackageUnits(std::move(pu)); + + wamon::TypeChecker tc(pu); + std::string reason; + bool succ = tc.CheckAll(reason); + EXPECT_EQ(succ, true) << reason; + + wamon::Interpreter interpreter(pu); + auto v = interpreter.FindVariableById("main$v"); + EXPECT_EQ(v->GetTypeInfo(), "main$Color"); + EXPECT_EQ(wamon::AsEnumVariable(v)->GetEnumItem(), "Red"); + + EXPECT_THROW(interpreter.ExecExpression(tc, "main", "v = enum Color2:Black"), wamon::WamonException); + EXPECT_THROW(interpreter.ExecExpression(tc, "main", "v = enum Color:Black"), wamon::WamonException); +}