diff --git a/src/Ast/Statements/ImplementSt.hpp b/src/Ast/Statements/ImplementSt.hpp index e69de29..4d021b1 100644 --- a/src/Ast/Statements/ImplementSt.hpp +++ b/src/Ast/Statements/ImplementSt.hpp @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include + +namespace Fig::Ast +{ + /* + + impl Readable for File + { + read() -> String + { + ... + } + + */ + struct ImplementMethod + { + FString name; + FunctionParameters paras; + BlockStatement body; + }; + + class ImplementAst final : public StatementAst + { + public: + FString interfaceName; + FString structName; + + std::vector methods; + + ImplementAst() + { + type = AstType::ImplementSt; + } + + ImplementAst(FString _interfaceName, FString _structName, std::vector _methods) : + interfaceName(std::move(_interfaceName)), structName(std::move(_structName)), methods(std::move(_methods)) + { + type = AstType::ImplementSt; + } + }; + + using Implement = std::shared_ptr; +}; \ No newline at end of file diff --git a/src/Ast/Statements/InterfaceDefSt.hpp b/src/Ast/Statements/InterfaceDefSt.hpp new file mode 100644 index 0000000..d062fa4 --- /dev/null +++ b/src/Ast/Statements/InterfaceDefSt.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + + +namespace Fig::Ast +{ + /* + + interface Readable + { + read() -> String + { + // default + } + + flush() -> Null; // non-default + } + + */ + + + struct InterfaceMethod + { + FString name; + FunctionParameters paras; + FString returnType; + + BlockStatement defaultBody = nullptr; // nullptr is non-default func + + bool hasDefaultBody() const + { + return defaultBody != nullptr; + } + }; + + class InterfaceDefAst final : public StatementAst + { + public: + FString name; + std::vector methods; + std::vector parents; // Feature, NOT NOW + bool isPublic; + + InterfaceDefAst() + { + type = AstType::InterfaceDefSt; + } + + InterfaceDefAst(FString _name, std::vector _methods, bool _isPublic) : + name(std::move(_name)), methods(std::move(_methods)), isPublic(_isPublic) + { + type = AstType::InterfaceDefSt; + } + }; + + using InterfaceDef = std::shared_ptr; +}; \ No newline at end of file diff --git a/src/Ast/ast.hpp b/src/Ast/ast.hpp index 0a038b4..c2b7741 100644 --- a/src/Ast/ast.hpp +++ b/src/Ast/ast.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/src/Ast/astBase.hpp b/src/Ast/astBase.hpp index d64a165..05e8f51 100644 --- a/src/Ast/astBase.hpp +++ b/src/Ast/astBase.hpp @@ -44,6 +44,7 @@ namespace Fig::Ast VarDefSt, FunctionDefSt, StructSt, + InterfaceDefSt, ImplementSt, IfSt, diff --git a/src/Ast/functionParameters.hpp b/src/Ast/functionParameters.hpp index 3c8ae5c..4f1e6c6 100644 --- a/src/Ast/functionParameters.hpp +++ b/src/Ast/functionParameters.hpp @@ -4,6 +4,8 @@ #include #include +#include + namespace Fig::Ast { struct FunctionParameters // for define @@ -26,7 +28,6 @@ namespace Fig::Ast FunctionParameters() { - } FunctionParameters(PosParasType _posParas, DefParasType _defParas) { @@ -43,5 +44,52 @@ namespace Fig::Ast { return posParas.size() + defParas.size(); } + + bool operator==(const FunctionParameters &other) const + { + return posParas == other.posParas && defParas == other.defParas && variadicPara == other.variadicPara && variadic == other.variadic; + } + + FString toString() const + { + if (variadic) + { + return FString(variadicPara + u8"..."); + } + static const auto posParasToString = [this]() { + FString out; + for (auto &p : posParas) + { + out += p.first; + if (!p.second.empty()) + { + out += FString(u8":" + p.second); + } + out += u8","; + } + out.pop_back(); + return out; + }; + static const auto defParasToString = [this]() { + FString out; + for (auto &p : defParas) + { + out += p.first; + if (!p.second.first.empty()) + { + out += FString(u8":" + p.second.first); + } + if (p.second.second != nullptr) + { + out += u8"="; + out += p.second.second->toString(); + } + out += u8","; + } + out.pop_back(); + return out; + }; + return FString(std::format("{},{}", posParasToString().toBasicString(), defParasToString().toBasicString())); + } }; -} \ No newline at end of file +} // namespace Fig::Ast \ No newline at end of file diff --git a/src/Context/context.hpp b/src/Context/context.hpp index 1c87db9..7b60d9d 100644 --- a/src/Context/context.hpp +++ b/src/Context/context.hpp @@ -1,5 +1,8 @@ #pragma once +#include "Value/interface.hpp" +#include +#include #include #include #include @@ -12,6 +15,14 @@ namespace Fig { + struct ImplRecord + { + TypeInfo interfaceType; + TypeInfo structType; + + std::unordered_map implMethods; + }; + class Context : public std::enable_shared_from_this { private: @@ -20,7 +31,9 @@ namespace Fig std::unordered_map functions; std::unordered_map functionNames; - // std::unordered_map structTypeNames; + + // implRegistry + std::unordered_map, TypeInfoHash> implRegistry; public: ContextPtr parent; @@ -28,7 +41,7 @@ namespace Fig Context(const Context &) = default; Context(const FString &name, ContextPtr p = nullptr) : scopeName(name), parent(p) {} - + void setParent(ContextPtr _parent) { parent = _parent; @@ -49,6 +62,7 @@ namespace Fig variables.insert(c.variables.begin(), c.variables.end()); functions.insert(c.functions.begin(), c.functions.end()); functionNames.insert(c.functionNames.begin(), c.functionNames.end()); + implRegistry.insert(c.implRegistry.begin(), c.implRegistry.end()); // structTypeNames.insert(c.structTypeNames.begin(), c.structTypeNames.end()); } @@ -243,6 +257,169 @@ namespace Fig } return false; } + + bool hasImplRegisted(const TypeInfo &structType, const TypeInfo &interfaceType) const + { + auto it = implRegistry.find(structType); + if (it != implRegistry.end()) + { + for (auto &r : it->second) + { + if (r.interfaceType == interfaceType) + return true; + } + } + return parent && parent->hasImplRegisted(structType, interfaceType); + } + + std::optional getImplRecord(const TypeInfo &structType, const TypeInfo &interfaceType) const + { + auto it = implRegistry.find(structType); + if (it != implRegistry.end()) + { + for (auto &r : it->second) + { + if (r.interfaceType == interfaceType) + return r; + } + } + + if (parent) + return parent->getImplRecord(structType, interfaceType); + + return std::nullopt; + } + + void setImplRecord(const TypeInfo &structType, const TypeInfo &interfaceType, const ImplRecord &record) + { + auto &list = implRegistry[structType]; + + for (auto &r : list) + { + if (r.interfaceType == interfaceType) + return; + } + + list.push_back(record); // order is the level + } + + bool hasMethodImplemented(const TypeInfo &structType, const FString &functionName) const + { + auto it = implRegistry.find(structType); + if (it == implRegistry.end()) + return false; + + for (auto &record : it->second) + { + if (record.implMethods.contains(functionName)) + return true; + } + + return false; + } + + bool hasDefaultImplementedMethod(const TypeInfo &structType, const FString &functionName) const + { + auto it = implRegistry.find(structType); + if (it == implRegistry.end()) + return false; + + std::vector implementedInterfaces; + for (auto &record : it->second) + implementedInterfaces.push_back(record.interfaceType); + + for (auto &[_, slot] : variables) + { + if (!slot->value->is()) + continue; + + InterfaceType &interface = slot->value->as(); + + bool implemented = + std::any_of( + implementedInterfaces.begin(), + implementedInterfaces.end(), + [&](const TypeInfo &ti) { return ti == interface.type; }); + + if (!implemented) + continue; + + for (auto &method : interface.methods) + { + if (method.name == functionName && method.hasDefaultBody()) + return true; + } + } + + return false; + } + + Function getDefaultImplementedMethod(const TypeInfo &structType, const FString &functionName) + { + // O(N²) + // SLOW + // wwww (sad) + + // huh + // just let it be SLOW + // i dont give a fuck + + auto it = implRegistry.find(structType); + if (it == implRegistry.end()) + assert(false); + + std::vector implementedInterfaces; + for (auto &record : it->second) + implementedInterfaces.push_back(record.interfaceType); + + for (auto &[_, slot] : variables) + { + if (!slot->value->is()) + continue; + + InterfaceType &interface = slot->value->as(); + + bool implemented = + std::any_of( + implementedInterfaces.begin(), + implementedInterfaces.end(), + [&](const TypeInfo &ti) { return ti == interface.type; }); + + if (!implemented) + continue; + + for (auto &method : interface.methods) + { + if (method.name == functionName) + { + if (!method.hasDefaultBody()) + assert(false); + + return Function( + method.paras, + TypeInfo(method.returnType), + method.defaultBody, + shared_from_this()); + } + } + } + + assert(false); + } + + const Function &getImplementedMethod(const TypeInfo &structType, const FString &functionName) const + { + const auto &list = implRegistry.at(structType); + for (auto &record : list) + { + auto it = record.implMethods.find(functionName); + if (it != record.implMethods.end()) + return it->second; + } + + assert(false); + } + void printStackTrace(std::ostream &os = std::cerr, int indent = 0) const { const Context *ctx = this; diff --git a/src/Evaluator/evaluator.cpp b/src/Evaluator/evaluator.cpp index f25b7bc..2ad7b10 100644 --- a/src/Evaluator/evaluator.cpp +++ b/src/Evaluator/evaluator.cpp @@ -1,3 +1,14 @@ +#include "Value/VariableSlot.hpp" +#include "Value/value.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -8,21 +19,14 @@ #include #include +#include namespace Fig { - bool Evaluator::isTypeMatch(const TypeInfo &expected, ObjectPtr obj) + + bool Evaluator::isInterfaceSignatureMatch(const Ast::ImplementMethod &implMethod, const Ast::InterfaceMethod &ifMethod) { - if (expected == ValueType::Any) - return true; - - TypeInfo actual = obj->getTypeInfo(); - - if (actual != ValueType::StructInstance) - return expected == actual; - - const StructInstance &si = obj->as(); - return si.parentType == expected; + return implMethod.name == ifMethod.name && implMethod.paras == ifMethod.paras; } LvObject Evaluator::evalVarExpr(Ast::VarExpr var, ContextPtr ctx) @@ -32,7 +36,7 @@ namespace Fig { throw EvaluatorError(u8"UndeclaredIdentifierError", name, var); } - return LvObject(ctx->get(name)); + return LvObject(ctx->get(name), ctx); } LvObject Evaluator::evalMemberExpr(Ast::MemberExpr me, ContextPtr ctx) { @@ -45,7 +49,8 @@ namespace Fig if (mod.ctx->contains(member) && mod.ctx->isVariablePublic(member)) { return LvObject( - mod.ctx->get(member)); + mod.ctx->get(member), + ctx); } else { @@ -61,13 +66,14 @@ namespace Fig if (baseVal->hasMemberFunction(member)) { return LvObject(std::make_shared( - member, - std::make_shared( - Function( - baseVal->getMemberFunction(member), - baseVal->getMemberFunctionParaCount(member))), - ValueType::Function, - AccessModifier::PublicConst)); // fake l-value + member, + std::make_shared( + Function( + baseVal->getMemberFunction(member), + baseVal->getMemberFunctionParaCount(member))), + ValueType::Function, + AccessModifier::PublicConst), + ctx); // fake l-value } if (baseVal->getTypeInfo() != ValueType::StructInstance) // and not member function found { @@ -80,17 +86,40 @@ namespace Fig me->base); } const StructInstance &si = baseVal->as(); - if (!si.localContext->containsInThisScope(member) || !si.localContext->isVariablePublic(member)) + if (ctx->hasMethodImplemented(si.parentType, member)) + { + return LvObject(std::make_shared( + member, + std::make_shared( + ctx->getImplementedMethod(si.parentType, member)), + ValueType::Function, + AccessModifier::PublicConst), + ctx); + } + else if (si.localContext->containsInThisScope(member) && si.localContext->isVariablePublic(member)) + { + return LvObject(si.localContext->get(member), ctx); + } + else if (ctx->hasDefaultImplementedMethod(si.parentType, member)) + { + return LvObject(std::make_shared( + member, + std::make_shared( + ctx->getDefaultImplementedMethod(si.parentType, member)), + ValueType::Function, + AccessModifier::PublicConst), + ctx); + } + else { throw EvaluatorError( u8"NoAttributeError", std::format( - "`{}` has not attribute '{}'", + "`{}` has not attribute '{}' and no interfaces have been implemented it", baseVal->toString().toBasicString(), member.toBasicString()), me->base); } - return LvObject(si.localContext->get(member)); } LvObject Evaluator::evalIndexExpr(Ast::IndexExpr ie, ContextPtr ctx) { @@ -125,14 +154,16 @@ namespace Fig return LvObject( base.get(), indexVal, - LvObject::Kind::ListElement); + LvObject::Kind::ListElement, + ctx); } else if (type == ValueType::Map) { return LvObject( base.get(), index, - LvObject::Kind::MapElement); + LvObject::Kind::MapElement, + ctx); } else if (type == ValueType::String) { @@ -160,7 +191,8 @@ namespace Fig return LvObject( base.get(), indexVal, - LvObject::Kind::StringElement); + LvObject::Kind::StringElement, + ctx); } else { @@ -450,7 +482,7 @@ namespace Fig TypeInfo expectedType(fnParas.posParas[i].second); // look up type info, if exists a type with the name, use it, else throw ObjectPtr argVal = eval(fnArgs.argv[i], ctx); TypeInfo actualType = argVal->getTypeInfo(); - if (!isTypeMatch(expectedType, argVal)) + if (!isTypeMatch(expectedType, argVal, ctx)) { throw EvaluatorError( u8"ArgumentTypeMismatchError", @@ -467,10 +499,10 @@ namespace Fig for (; i < fnArgs.getLength(); i++) { size_t defParamIndex = i - fnParas.posParas.size(); - TypeInfo expectedType = fnParas.defParas[defParamIndex].second.first; + TypeInfo expectedType(fnParas.defParas[defParamIndex].second.first); ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, ctx); - if (!isTypeMatch(expectedType, defaultVal)) + if (!isTypeMatch(expectedType, defaultVal, ctx)) { throw EvaluatorError( u8"DefaultParameterTypeError", @@ -484,7 +516,7 @@ namespace Fig ObjectPtr argVal = eval(fnArgs.argv[i], ctx); TypeInfo actualType = argVal->getTypeInfo(); - if (!isTypeMatch(expectedType, argVal)) + if (!isTypeMatch(expectedType, argVal, ctx)) { throw EvaluatorError( u8"ArgumentTypeMismatchError", @@ -512,13 +544,13 @@ namespace Fig if (j < fnParas.posParas.size()) { paramName = fnParas.posParas[j].first; - paramType = fnParas.posParas[j].second; + paramType = TypeInfo(fnParas.posParas[j].second); } else { size_t defParamIndex = j - fnParas.posParas.size(); paramName = fnParas.defParas[defParamIndex].first; - paramType = fnParas.defParas[defParamIndex].second.first; + paramType = TypeInfo(fnParas.defParas[defParamIndex].second.first); } AccessModifier argAm = AccessModifier::Const; newContext->def(paramName, paramType, argAm, evaluatedArgs.argv[j]); @@ -553,7 +585,7 @@ namespace Fig break; } } - if (!isTypeMatch(fnStruct.retType, retVal)) + if (!isTypeMatch(fnStruct.retType, retVal, ctx)) { throw EvaluatorError( u8"ReturnTypeMismatchError", @@ -741,7 +773,7 @@ namespace Fig ObjectPtr defaultVal = eval(field.defaultValue, ctx); // it can't be null here // type check - if (!isTypeMatch(expectedType, defaultVal)) + if (!isTypeMatch(expectedType, defaultVal, ctx)) { throw EvaluatorError( u8"StructFieldTypeMismatchError", @@ -759,7 +791,7 @@ namespace Fig } const ObjectPtr &argVal = evaluatedArgs[i].second; - if (!isTypeMatch(expectedType, argVal)) + if (!isTypeMatch(expectedType, argVal, ctx)) { throw EvaluatorError( u8"StructFieldTypeMismatchError", @@ -797,7 +829,7 @@ namespace Fig // type check const TypeInfo &expectedType = field.type; - if (!isTypeMatch(expectedType, defaultVal)) + if (!isTypeMatch(expectedType, defaultVal, ctx)) { throw EvaluatorError( u8"StructFieldTypeMismatchError", @@ -814,7 +846,7 @@ namespace Fig continue; } const ObjectPtr &argVal = evaluatedArgs[i].second; - if (!isTypeMatch(field.type, argVal)) + if (!isTypeMatch(field.type, argVal, ctx)) { throw EvaluatorError( u8"StructFieldTypeMismatchError", @@ -918,7 +950,7 @@ namespace Fig else if (!declaredTypeName.empty()) { declaredType = TypeInfo(declaredTypeName); - if (value != nullptr && !isTypeMatch(declaredType, value)) + if (value != nullptr && !isTypeMatch(declaredType, value, ctx)) { throw EvaluatorError( u8"TypeError", @@ -1031,6 +1063,194 @@ namespace Fig return StatementResult::normal(); } + case InterfaceDefSt: { + auto ifd = std::dynamic_pointer_cast(stmt); + assert(ifd != nullptr); + + const FString &interfaceName = ifd->name; + + if (ctx->containsInThisScope(interfaceName)) + { + throw EvaluatorError( + u8"RedeclarationError", + std::format("Interface `{}` already declared in this scope", + interfaceName.toBasicString()), + ifd); + } + TypeInfo type(interfaceName, true); // register interface + ctx->def( + interfaceName, + type, + (ifd->isPublic ? AccessModifier::PublicConst : AccessModifier::Const), + std::make_shared(InterfaceType( + type, + ifd->methods))); + return StatementResult::normal(); + } + + case ImplementSt: { + auto ip = std::dynamic_pointer_cast(stmt); + assert(ip != nullptr); + + TypeInfo structType(ip->structName); + TypeInfo interfaceType(ip->interfaceName); + if (ctx->hasImplRegisted(structType, interfaceType)) + { + throw EvaluatorError( + u8"DuplicateImplError", + std::format( + "Duplicate implement `{}` for `{}`", + interfaceType.toString().toBasicString(), + structType.toString().toBasicString()), + ip); + } + if (!ctx->contains(ip->interfaceName)) + { + throw EvaluatorError( + u8"InterfaceNotFoundError", + std::format("Interface '{}' not found", ip->interfaceName.toBasicString()), + ip); + } + if (!ctx->contains(ip->structName)) + { + throw EvaluatorError( + u8"StructNotFoundError", + std::format("Struct '{}' not found", ip->structName.toBasicString()), + ip); + } + auto interfaceSlot = ctx->get(ip->interfaceName); + auto structSlot = ctx->get(ip->structName); + + LvObject interfaceLv(interfaceSlot, ctx); + LvObject structLv(structSlot, ctx); + + ObjectPtr interfaceObj = interfaceLv.get(); + ObjectPtr structTypeObj = structLv.get(); + + if (!interfaceObj->is()) + { + throw EvaluatorError( + u8"NotAInterfaceError", + std::format("Variable `{}` is not a interface", ip->interfaceName.toBasicString()), + ip); + } + if (!structTypeObj->is()) + { + throw EvaluatorError( + u8"NotAStructType", + std::format("Variable `{}` is not a struct type", ip->structName.toBasicString()), + ip); + } + auto &implementMethods = ip->methods; + InterfaceType &interface = interfaceObj->as(); + + // ===== interface implementation validation ===== + ImplRecord record{interfaceType, structType, {}}; + + std::unordered_map ifaceMethods; + for (auto &m : interface.methods) + { + if (ifaceMethods.contains(m.name)) + { + throw EvaluatorError( + u8"InterfaceDuplicateMethodError", + std::format("Interface '{}' has duplicate method '{}'", + interfaceType.toString().toBasicString(), + m.name.toBasicString()), + ip); + } + ifaceMethods[m.name] = m; + } + + std::unordered_set implemented; + + for (auto &implMethod : implementMethods) + { + const FString &name = implMethod.name; + + // ---- redundant impl ---- + if (!ifaceMethods.contains(name)) + { + throw EvaluatorError( + u8"RedundantImplementationError", + std::format("Struct '{}' implements extra method '{}' " + "which is not required by interface '{}'", + structType.toString().toBasicString(), + name.toBasicString(), + interfaceType.toString().toBasicString()), + ip); + } + + if (implemented.contains(name)) + { + throw EvaluatorError( + u8"DuplicateImplementMethodError", + std::format("Duplicate implement method '{}'", + name.toBasicString()), + ip); + } + + auto &ifMethod = ifaceMethods[name]; + + // ---- signature check ---- + if (!isInterfaceSignatureMatch(implMethod, ifMethod)) + { + throw EvaluatorError( + u8"InterfaceSignatureMismatch", + std::format( + "Interface method '{}({})' signature mismatch with " + "implementation '{}({})'", + ifMethod.name.toBasicString(), + ifMethod.paras.toString().toBasicString(), + implMethod.name.toBasicString(), + implMethod.paras.toString().toBasicString()), + ip); + } + + if (ctx->hasMethodImplemented(structType, name)) + { + throw EvaluatorError( + u8"DuplicateImplementMethodError", + std::format( + "Method '{}' already implemented by another interface " + "for struct '{}'", + name.toBasicString(), + structType.toString().toBasicString()), + ip); + } + + implemented.insert(name); + + record.implMethods[name] = Function( + implMethod.paras, + TypeInfo(ifMethod.returnType), + implMethod.body, + ctx); + } + + for (auto &m : interface.methods) + { + if (implemented.contains(m.name)) + continue; + + if (m.hasDefaultBody()) + continue; + + throw EvaluatorError( + u8"MissingImplementationError", + std::format( + "Struct '{}' does not implement required interface method '{}' " + "and interface '{}' provides no default implementation", + structType.toString().toBasicString(), + m.name.toBasicString(), + interfaceType.toString().toBasicString()), + ip); + } + + ctx->setImplRecord(structType, interfaceType, record); + return StatementResult::normal(); + } + case IfSt: { auto ifSt = std::dynamic_pointer_cast(stmt); ObjectPtr condVal = eval(ifSt->condition, ctx); diff --git a/src/Evaluator/evaluator.hpp b/src/Evaluator/evaluator.hpp index 6d18b05..5b0cc9b 100644 --- a/src/Evaluator/evaluator.hpp +++ b/src/Evaluator/evaluator.hpp @@ -1,3 +1,6 @@ +#include "Ast/Statements/ImplementSt.hpp" +#include "Ast/Statements/InterfaceDefSt.hpp" +#include "Value/Type.hpp" #include #include @@ -102,7 +105,7 @@ namespace Fig } } - bool isTypeMatch(const TypeInfo &, ObjectPtr); + bool isInterfaceSignatureMatch(const Ast::ImplementMethod &, const Ast::InterfaceMethod &); /* Left-value eval*/ LvObject evalVarExpr(Ast::VarExpr, ContextPtr); diff --git a/src/Parser/parser.cpp b/src/Parser/parser.cpp index f0f81d6..695d051 100644 --- a/src/Parser/parser.cpp +++ b/src/Parser/parser.cpp @@ -1,3 +1,8 @@ +#include "Ast/Statements/ImplementSt.hpp" +#include "Ast/astBase.hpp" +#include "Ast/functionParameters.hpp" +#include "Error/error.hpp" +#include "Token/token.hpp" #include namespace Fig @@ -365,6 +370,114 @@ namespace Fig } return makeAst(isPublic, structName, fields, makeAst(stmts)); } + + Ast::InterfaceDef Parser::__parseInterfaceDef(bool isPublic) + { + // entry: current is interface name + FString interfaceName = currentToken().getValue(); + next(); // consume name + expect(TokenType::LeftBrace); // `{ + next(); // consume `{` + + std::vector methods; + + while (true) + { + if (isThis(TokenType::RightBrace)) + { + next(); // consume `}` + break; + } + if (isThis(TokenType::Identifier)) + { + FString funcName = currentToken().getValue(); + next(); // consume func name + + expect(TokenType::LeftParen); + Ast::FunctionParameters paras = __parseFunctionParameters(); + + expect(TokenType::RightArrow); // -> + next(); // consume `->` + + expect(TokenType::Identifier, u8"return type"); + FString returnType = currentToken().getValue(); + next(); // consume return type + + if (isThis(TokenType::LeftBrace)) + { + Ast::BlockStatement block = __parseBlockStatement(); + + methods.push_back(Ast::InterfaceMethod( + funcName, + paras, + returnType, + block)); + continue; + } + expect(TokenType::Semicolon); + next(); // consume `;` + + methods.push_back(Ast::InterfaceMethod( + funcName, + paras, + returnType)); + } + else + { + throw SyntaxError(FString(u8"Invalid syntax"), currentAAI.line, currentAAI.column); + } + } + return makeAst(interfaceName, methods, isPublic); + } + + Ast::Implement Parser::__parseImplement() + { + // entry: current is `impl` + next(); // consume `impl` + expect(TokenType::Identifier, u8"interface name"); + FString interfaceName = currentToken().getValue(); + next(); // consume interface name + + expect(TokenType::For); + next(); // consume `for` + + expect(TokenType::Identifier, u8"struct name"); + FString structName = currentToken().getValue(); + next(); // consume name + expect(TokenType::LeftBrace); // { + next(); // consume `{` + + std::vector methods; + + while (true) + { + if (isThis(TokenType::RightBrace)) + { + next(); // consume `}` + break; + } + if (isThis(TokenType::Identifier)) + { + FString funcName = currentToken().getValue(); + next(); // consume func name + expect(TokenType::LeftParen); + Ast::FunctionParameters paras = __parseFunctionParameters(); + expect(TokenType::LeftBrace); + Ast::BlockStatement body = __parseBlockStatement(); + methods.push_back(Ast::ImplementMethod( + funcName, + paras, + body)); + } + else + { + throw SyntaxError(FString(u8"Invalid syntax"), currentAAI.line, currentAAI.column); + } + } + + return makeAst(interfaceName, structName, methods); + } + Ast::Statement Parser::__parseStatement() { Ast::Statement stmt; @@ -389,9 +502,13 @@ namespace Fig { stmt = __parseStructDef(true); } + else if (isThis(TokenType::Interface)) + { + stmt = __parseInterfaceDef(true); + } else { - throwAddressableError(FString(u8"Expected `var`, `const`, `function` or `struct` after `public`")); + throwAddressableError(FString(u8"Expected `var`, `const`, `function`, `struct` or `interface` after `public`")); } } else if (isThis(TokenType::Variable) || isThis(TokenType::Const)) @@ -409,6 +526,16 @@ namespace Fig next(); stmt = __parseStructDef(false); } + else if (isThis(TokenType::Interface)) + { + expectPeek(TokenType::Identifier, u8"interface name"); + next(); + stmt = __parseInterfaceDef(false); + } + else if (isThis(TokenType::Implement)) + { + stmt = __parseImplement(); + } else if (isThis(TokenType::If)) { stmt = __parseIf(); @@ -501,7 +628,7 @@ namespace Fig expect(TokenType::RightParen); next(); // consume `)` } - else + else { elifCondition = parseExpression(0); } diff --git a/src/Parser/parser.hpp b/src/Parser/parser.hpp index 4dca735..eeddc49 100644 --- a/src/Parser/parser.hpp +++ b/src/Parser/parser.hpp @@ -313,6 +313,8 @@ namespace Fig Ast::VarExpr __parseVarExpr(FString); Ast::FunctionDef __parseFunctionDef(bool); // entry: current is Token::Identifier (isPublic: Bool) Ast::StructDef __parseStructDef(bool); // entry: current is Token::Identifier (struct name) arg(isPublic: bool) + Ast::InterfaceDef __parseInterfaceDef(bool); // entry: current is Token::Identifier (interface name) arg(isPublic: bool) + Ast::Implement __parseImplement(); // entry: current is `impl` Ast::BinaryExpr __parseInfix(Ast::Expression, Ast::Operator, Precedence); Ast::UnaryExpr __parsePrefix(Ast::Operator, Precedence); diff --git a/src/Value/LvObject.hpp b/src/Value/LvObject.hpp index f971ea4..a1b1686 100644 --- a/src/Value/LvObject.hpp +++ b/src/Value/LvObject.hpp @@ -22,18 +22,20 @@ namespace Fig ObjectPtr mapIndex; - LvObject(std::shared_ptr _slot) : - slot(std::move(_slot)) + ContextPtr ctx; + + LvObject(std::shared_ptr _slot, ContextPtr _ctx) : + slot(std::move(_slot)), ctx(_ctx) { kind = Kind::Variable; } - LvObject(ObjectPtr _v, size_t _index, Kind _kind) : - value(_v), numIndex(_index) + LvObject(ObjectPtr _v, size_t _index, Kind _kind, ContextPtr _ctx) : + value(_v), numIndex(_index), ctx(_ctx) { kind = _kind; } - LvObject(ObjectPtr _v, ObjectPtr _index, Kind _kind) : - value(_v), mapIndex(_index) + LvObject(ObjectPtr _v, ObjectPtr _index, Kind _kind, ContextPtr _ctx) : + value(_v), mapIndex(_index), ctx(_ctx) { kind = _kind; } @@ -78,7 +80,7 @@ namespace Fig if (kind == Kind::Variable) { auto s = resolve(slot); - if (s->declaredType != ValueType::Any && s->declaredType != v->getTypeInfo()) + if (!isTypeMatch(s->declaredType, v, ctx)) { throw RuntimeError( FString( diff --git a/src/Value/Type.hpp b/src/Value/Type.hpp index 20f83d8..834926c 100644 --- a/src/Value/Type.hpp +++ b/src/Value/Type.hpp @@ -35,7 +35,7 @@ namespace Fig } TypeInfo(); - TypeInfo(FString _name, bool reg = false); + explicit TypeInfo(const FString &_name, bool reg = false); TypeInfo(const TypeInfo &other) = default; bool operator==(const TypeInfo &other) const @@ -68,7 +68,7 @@ namespace Fig extern const TypeInfo List; extern const TypeInfo Map; extern const TypeInfo Module; - // extern const TypeInfo Tuple; + extern const TypeInfo InterfaceType; using IntClass = int64_t; using DoubleClass = double; diff --git a/src/Value/interface.hpp b/src/Value/interface.hpp new file mode 100644 index 0000000..59697c5 --- /dev/null +++ b/src/Value/interface.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +#include + +namespace Fig +{ + struct InterfaceType + { + TypeInfo type; + std::vector methods; + + bool operator==(const InterfaceType &other) const + { + return type == other.type; // only compare type info (chain -> typeinfo.id) + } + }; +} \ No newline at end of file diff --git a/src/Value/value.cpp b/src/Value/value.cpp index 0a27964..c7b3db9 100644 --- a/src/Value/value.cpp +++ b/src/Value/value.cpp @@ -1,3 +1,4 @@ +#include "Value/Type.hpp" #include #include @@ -7,12 +8,14 @@ namespace Fig { std::map TypeInfo::typeMap = {}; - TypeInfo::TypeInfo() : // only allow use in evaluate time !! <---- dynamic type system requirement - id(1), name(FString(u8"Any")) {} - TypeInfo::TypeInfo(FString _name, bool reg) + TypeInfo::TypeInfo() : // only allow use in evaluate time !! <---- dynamic type system requirement + id(1), name(FString(u8"Any")) + { + } + TypeInfo::TypeInfo(const FString &_name, bool reg) { static size_t id_count = 0; - name = std::move(_name); + name = _name; // std::cerr << "TypeInfo constructor called for type name: " << name.toBasicString() << "\n"; if (reg) { @@ -21,10 +24,17 @@ namespace Fig } else { + if (!typeMap.contains(_name)) + { + throw RuntimeError(FString(std::format( + "No type named '{}'", + _name.toBasicString()))); + // *this = ValueType::String; + } id = typeMap.at(name); // may throw } } - + size_t ValueKeyHash::operator()(const ValueKey &key) const { { @@ -69,7 +79,7 @@ namespace Fig } } - FString prettyType(ObjectPtr obj) + FString prettyType(std::shared_ptr obj) { auto t = obj->getTypeInfo(); if (t == ValueType::StructInstance) @@ -77,16 +87,51 @@ namespace Fig return t.toString(); } - const TypeInfo ValueType::Any(FString(u8"Any"), true); // id: 1 - const TypeInfo ValueType::Null(FString(u8"Null"), true); // id: 2 - const TypeInfo ValueType::Int(FString(u8"Int"), true); // id: 3 - const TypeInfo ValueType::String(FString(u8"String"), true); // id: 4 - const TypeInfo ValueType::Bool(FString(u8"Bool"), true); // id: 5 - const TypeInfo ValueType::Double(FString(u8"Double"), true); // id: 6 - const TypeInfo ValueType::Function(FString(u8"Function"), true); // id: 7 - const TypeInfo ValueType::StructType(FString(u8"StructType"), true); // id: 8 + const TypeInfo ValueType::Any(FString(u8"Any"), true); // id: 1 + const TypeInfo ValueType::Null(FString(u8"Null"), true); // id: 2 + const TypeInfo ValueType::Int(FString(u8"Int"), true); // id: 3 + const TypeInfo ValueType::String(FString(u8"String"), true); // id: 4 + const TypeInfo ValueType::Bool(FString(u8"Bool"), true); // id: 5 + const TypeInfo ValueType::Double(FString(u8"Double"), true); // id: 6 + const TypeInfo ValueType::Function(FString(u8"Function"), true); // id: 7 + const TypeInfo ValueType::StructType(FString(u8"StructType"), true); // id: 8 const TypeInfo ValueType::StructInstance(FString(u8"StructInstance"), true); // id: 9 - const TypeInfo ValueType::List(FString(u8"List"), true); // id: 10 - const TypeInfo ValueType::Map(FString(u8"Map"), true); // id: 11 - const TypeInfo ValueType::Module(FString(u8"Module"), true); // id: 12 + const TypeInfo ValueType::List(FString(u8"List"), true); // id: 10 + const TypeInfo ValueType::Map(FString(u8"Map"), true); // id: 11 + const TypeInfo ValueType::Module(FString(u8"Module"), true); // id: 12 + const TypeInfo ValueType::InterfaceType(FString(u8"InterfaceType"), true); // id: 13 + + + + bool implements(const TypeInfo &structType, const TypeInfo &interfaceType, ContextPtr ctx) + { + return ctx->hasImplRegisted(structType, interfaceType); + } + + bool isTypeMatch(const TypeInfo &expected, ObjectPtr obj, ContextPtr ctx) + { + if (expected == ValueType::Any) + return true; + + TypeInfo actual = obj->getTypeInfo(); + + if (obj->is()) + { + const StructInstance &si = obj->as(); + if (si.parentType == expected) + { + return true; + } + if (implements(si.parentType, expected, ctx)) + { + return true; + } + return false; + } + else + { + return expected == actual; + } + } + } // namespace Fig \ No newline at end of file diff --git a/src/Value/value.hpp b/src/Value/value.hpp index 79d0e79..be55814 100644 --- a/src/Value/value.hpp +++ b/src/Value/value.hpp @@ -1,11 +1,13 @@ #pragma once #include +#include #include #include #include #include #include +#include #include #include #include @@ -33,7 +35,7 @@ namespace Fig using ObjectPtr = std::shared_ptr; using List = std::vector; - FString prettyType(ObjectPtr obj); + FString prettyType(std::shared_ptr obj); struct ValueKey { @@ -48,7 +50,10 @@ namespace Fig }; using Map = std::unordered_map; - class Object + bool isTypeMatch(const TypeInfo &, ObjectPtr, ContextPtr); + bool implements(const TypeInfo &, const TypeInfo &, ContextPtr); + + class Object : public std::enable_shared_from_this { public: using VariantType = std::variant< @@ -62,7 +67,8 @@ namespace Fig StructInstance, List, Map, - Module>; + Module, + InterfaceType>; std::unordered_map, TypeInfoHash> memberTypeFunctionsParas{ {ValueType::Null, {}}, {ValueType::Int, {}}, @@ -226,8 +234,8 @@ namespace Fig {u8"get", 1}, {u8"contains", 1}, }}, - {ValueType::Module, {}} - + {ValueType::Module, {}}, + {ValueType::InterfaceType, {}}, }; bool hasMemberFunction(const FString &name) const { @@ -268,6 +276,8 @@ namespace Fig data(m) {} Object(const Module &m) : data(m) {} + Object(const InterfaceType &i) : + data(i) {} Object(const Object &) = default; Object(Object &&) noexcept = default; @@ -364,6 +374,9 @@ namespace Fig else if constexpr (std::is_same_v) return ValueType::Module; + else if constexpr (std::is_same_v) + return ValueType::InterfaceType; + else return ValueType::Any; }, @@ -397,11 +410,11 @@ namespace Fig if (is()) return FString(u8"() + FString(u8"\" >"); if (is()) return as() ? FString(u8"true") : FString(u8"false"); if (is()) - return FString(std::format("", + return FString(std::format("", as().id, static_cast(&as()))); if (is()) - return FString(std::format("", + return FString(std::format("", as().type.toString().toBasicString(), static_cast(&as()))); if (is()) @@ -441,9 +454,17 @@ namespace Fig if (is()) { return FString(std::format( - "", + "", + as().name.toBasicString(), static_cast(&as()))); } + if (is()) + { + return FString(std::format( + "().type.toString().toBasicString(), + static_cast(&as()))); + } return FString(u8""); }