[VER] 0.3.3-alpha
[FEAT] interface + impl 支持! Duck Typing + 严格的检查让语言健壮 [FEAT][IMPL] 增加辅助函数 isTypeMatch等 [IMPL] TypeInfo构造函数FString 现在 explicit
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include <Ast/functionParameters.hpp>
|
||||
#include <Ast/Statements/FunctionDefSt.hpp>
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
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<ImplementMethod> methods;
|
||||
|
||||
ImplementAst()
|
||||
{
|
||||
type = AstType::ImplementSt;
|
||||
}
|
||||
|
||||
ImplementAst(FString _interfaceName, FString _structName, std::vector<ImplementMethod> _methods) :
|
||||
interfaceName(std::move(_interfaceName)), structName(std::move(_structName)), methods(std::move(_methods))
|
||||
{
|
||||
type = AstType::ImplementSt;
|
||||
}
|
||||
};
|
||||
|
||||
using Implement = std::shared_ptr<ImplementAst>;
|
||||
};
|
||||
59
src/Ast/Statements/InterfaceDefSt.hpp
Normal file
59
src/Ast/Statements/InterfaceDefSt.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/functionParameters.hpp>
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
|
||||
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<InterfaceMethod> methods;
|
||||
std::vector<FString> parents; // Feature, NOT NOW
|
||||
bool isPublic;
|
||||
|
||||
InterfaceDefAst()
|
||||
{
|
||||
type = AstType::InterfaceDefSt;
|
||||
}
|
||||
|
||||
InterfaceDefAst(FString _name, std::vector<InterfaceMethod> _methods, bool _isPublic) :
|
||||
name(std::move(_name)), methods(std::move(_methods)), isPublic(_isPublic)
|
||||
{
|
||||
type = AstType::InterfaceDefSt;
|
||||
}
|
||||
};
|
||||
|
||||
using InterfaceDef = std::shared_ptr<InterfaceDefAst>;
|
||||
};
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <Ast/Statements/IfSt.hpp>
|
||||
#include <Ast/Statements/ImplementSt.hpp>
|
||||
#include <Ast/Statements/ImportSt.hpp>
|
||||
#include <Ast/Statements/InterfaceDefSt.hpp>
|
||||
#include <Ast/Statements/FunctionDefSt.hpp>
|
||||
#include <Ast/Statements/ControlSt.hpp>
|
||||
#include <Ast/Statements/ExpressionStmt.hpp>
|
||||
|
||||
@@ -44,6 +44,7 @@ namespace Fig::Ast
|
||||
VarDefSt,
|
||||
FunctionDefSt,
|
||||
StructSt,
|
||||
InterfaceDefSt,
|
||||
ImplementSt,
|
||||
|
||||
IfSt,
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <Value/Type.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
|
||||
#include <format>
|
||||
|
||||
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()));
|
||||
}
|
||||
};
|
||||
} // namespace Fig::Ast
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Value/interface.hpp"
|
||||
#include <Value/Type.hpp>
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
@@ -12,6 +15,14 @@
|
||||
namespace Fig
|
||||
{
|
||||
|
||||
struct ImplRecord
|
||||
{
|
||||
TypeInfo interfaceType;
|
||||
TypeInfo structType;
|
||||
|
||||
std::unordered_map<FString, Function> implMethods;
|
||||
};
|
||||
|
||||
class Context : public std::enable_shared_from_this<Context>
|
||||
{
|
||||
private:
|
||||
@@ -20,7 +31,9 @@ namespace Fig
|
||||
|
||||
std::unordered_map<std::size_t, Function> functions;
|
||||
std::unordered_map<std::size_t, FString> functionNames;
|
||||
// std::unordered_map<std::size_t, FString> structTypeNames;
|
||||
|
||||
// implRegistry <Struct, ordered list of ImplRecord>
|
||||
std::unordered_map<TypeInfo, std::vector<ImplRecord>, TypeInfoHash> implRegistry;
|
||||
|
||||
public:
|
||||
ContextPtr 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<ImplRecord> 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<TypeInfo> implementedInterfaces;
|
||||
for (auto &record : it->second)
|
||||
implementedInterfaces.push_back(record.interfaceType);
|
||||
|
||||
for (auto &[_, slot] : variables)
|
||||
{
|
||||
if (!slot->value->is<InterfaceType>())
|
||||
continue;
|
||||
|
||||
InterfaceType &interface = slot->value->as<InterfaceType>();
|
||||
|
||||
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<TypeInfo> implementedInterfaces;
|
||||
for (auto &record : it->second)
|
||||
implementedInterfaces.push_back(record.interfaceType);
|
||||
|
||||
for (auto &[_, slot] : variables)
|
||||
{
|
||||
if (!slot->value->is<InterfaceType>())
|
||||
continue;
|
||||
|
||||
InterfaceType &interface = slot->value->as<InterfaceType>();
|
||||
|
||||
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;
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
#include "Value/VariableSlot.hpp"
|
||||
#include "Value/value.hpp"
|
||||
#include <Ast/AccessModifier.hpp>
|
||||
#include <Ast/Statements/ImplementSt.hpp>
|
||||
#include <Ast/Statements/InterfaceDefSt.hpp>
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Context/context_forward.hpp>
|
||||
#include <Error/error.hpp>
|
||||
#include <Value/Type.hpp>
|
||||
#include <Value/interface.hpp>
|
||||
#include <Value/structInstance.hpp>
|
||||
#include <Error/errorLog.hpp>
|
||||
#include <Evaluator/evaluator.hpp>
|
||||
#include <Evaluator/evaluator_error.hpp>
|
||||
@@ -8,21 +19,14 @@
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
|
||||
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<StructInstance>();
|
||||
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
|
||||
{
|
||||
@@ -67,7 +72,8 @@ namespace Fig
|
||||
baseVal->getMemberFunction(member),
|
||||
baseVal->getMemberFunctionParaCount(member))),
|
||||
ValueType::Function,
|
||||
AccessModifier::PublicConst)); // fake l-value
|
||||
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<StructInstance>();
|
||||
if (!si.localContext->containsInThisScope(member) || !si.localContext->isVariablePublic(member))
|
||||
if (ctx->hasMethodImplemented(si.parentType, member))
|
||||
{
|
||||
return LvObject(std::make_shared<VariableSlot>(
|
||||
member,
|
||||
std::make_shared<Object>(
|
||||
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<VariableSlot>(
|
||||
member,
|
||||
std::make_shared<Object>(
|
||||
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<Ast::InterfaceDefAst>(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<Object>(InterfaceType(
|
||||
type,
|
||||
ifd->methods)));
|
||||
return StatementResult::normal();
|
||||
}
|
||||
|
||||
case ImplementSt: {
|
||||
auto ip = std::dynamic_pointer_cast<Ast::ImplementAst>(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<InterfaceType>())
|
||||
{
|
||||
throw EvaluatorError(
|
||||
u8"NotAInterfaceError",
|
||||
std::format("Variable `{}` is not a interface", ip->interfaceName.toBasicString()),
|
||||
ip);
|
||||
}
|
||||
if (!structTypeObj->is<StructType>())
|
||||
{
|
||||
throw EvaluatorError(
|
||||
u8"NotAStructType",
|
||||
std::format("Variable `{}` is not a struct type", ip->structName.toBasicString()),
|
||||
ip);
|
||||
}
|
||||
auto &implementMethods = ip->methods;
|
||||
InterfaceType &interface = interfaceObj->as<InterfaceType>();
|
||||
|
||||
// ===== interface implementation validation =====
|
||||
ImplRecord record{interfaceType, structType, {}};
|
||||
|
||||
std::unordered_map<FString, Ast::InterfaceMethod> 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<FString> 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<Ast::IfSt>(stmt);
|
||||
ObjectPtr condVal = eval(ifSt->condition, ctx);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#include "Ast/Statements/ImplementSt.hpp"
|
||||
#include "Ast/Statements/InterfaceDefSt.hpp"
|
||||
#include "Value/Type.hpp"
|
||||
#include <Ast/ast.hpp>
|
||||
|
||||
#include <Context/context.hpp>
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <Parser/parser.hpp>
|
||||
|
||||
namespace Fig
|
||||
@@ -365,6 +370,114 @@ namespace Fig
|
||||
}
|
||||
return makeAst<Ast::StructDefSt>(isPublic, structName, fields, makeAst<Ast::BlockStatementAst>(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<Ast::InterfaceMethod> 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<Ast::InterfaceDefAst>(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<Ast::ImplementMethod> 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<Ast::ImplementAst>(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<SyntaxError>(FString(u8"Expected `var`, `const`, `function` or `struct` after `public`"));
|
||||
throwAddressableError<SyntaxError>(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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -22,18 +22,20 @@ namespace Fig
|
||||
|
||||
ObjectPtr mapIndex;
|
||||
|
||||
LvObject(std::shared_ptr<VariableSlot> _slot) :
|
||||
slot(std::move(_slot))
|
||||
ContextPtr ctx;
|
||||
|
||||
LvObject(std::shared_ptr<VariableSlot> _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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
20
src/Value/interface.hpp
Normal file
20
src/Value/interface.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Statements/InterfaceDefSt.hpp>
|
||||
#include <Value/Type.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct InterfaceType
|
||||
{
|
||||
TypeInfo type;
|
||||
std::vector<Ast::InterfaceMethod> methods;
|
||||
|
||||
bool operator==(const InterfaceType &other) const
|
||||
{
|
||||
return type == other.type; // only compare type info (chain -> typeinfo.id)
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "Value/Type.hpp"
|
||||
#include <Value/value.hpp>
|
||||
#include <Context/context.hpp>
|
||||
|
||||
@@ -8,11 +9,13 @@ namespace Fig
|
||||
std::map<FString, size_t> 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)
|
||||
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,6 +24,13 @@ 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
|
||||
}
|
||||
}
|
||||
@@ -69,7 +79,7 @@ namespace Fig
|
||||
}
|
||||
}
|
||||
|
||||
FString prettyType(ObjectPtr obj)
|
||||
FString prettyType(std::shared_ptr<const Object> obj)
|
||||
{
|
||||
auto t = obj->getTypeInfo();
|
||||
if (t == ValueType::StructInstance)
|
||||
@@ -89,4 +99,39 @@ namespace Fig
|
||||
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<StructInstance>())
|
||||
{
|
||||
const StructInstance &si = obj->as<StructInstance>();
|
||||
if (si.parentType == expected)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (implements(si.parentType, expected, ctx))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return expected == actual;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Fig
|
||||
@@ -1,11 +1,13 @@
|
||||
#pragma once
|
||||
#include <Value/function.hpp>
|
||||
#include <Value/interface.hpp>
|
||||
#include <Value/structType.hpp>
|
||||
#include <Value/structInstance.hpp>
|
||||
#include <Value/Type.hpp>
|
||||
#include <Value/valueError.hpp>
|
||||
#include <Value/module.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
@@ -33,7 +35,7 @@ namespace Fig
|
||||
using ObjectPtr = std::shared_ptr<Object>;
|
||||
using List = std::vector<ObjectPtr>;
|
||||
|
||||
FString prettyType(ObjectPtr obj);
|
||||
FString prettyType(std::shared_ptr<const Object> obj);
|
||||
|
||||
struct ValueKey
|
||||
{
|
||||
@@ -48,7 +50,10 @@ namespace Fig
|
||||
};
|
||||
using Map = std::unordered_map<ValueKey, ObjectPtr, ValueKeyHash>;
|
||||
|
||||
class Object
|
||||
bool isTypeMatch(const TypeInfo &, ObjectPtr, ContextPtr);
|
||||
bool implements(const TypeInfo &, const TypeInfo &, ContextPtr);
|
||||
|
||||
class Object : public std::enable_shared_from_this<Object>
|
||||
{
|
||||
public:
|
||||
using VariantType = std::variant<
|
||||
@@ -62,7 +67,8 @@ namespace Fig
|
||||
StructInstance,
|
||||
List,
|
||||
Map,
|
||||
Module>;
|
||||
Module,
|
||||
InterfaceType>;
|
||||
|
||||
std::unordered_map<TypeInfo,
|
||||
std::unordered_map<FString,
|
||||
@@ -207,7 +213,9 @@ namespace Fig
|
||||
map.contains(index));
|
||||
}},
|
||||
}},
|
||||
{ValueType::Module, {}}};
|
||||
{ValueType::Module, {}},
|
||||
{ValueType::InterfaceType, {}},
|
||||
};
|
||||
std::unordered_map<TypeInfo, std::unordered_map<FString, int>, 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<T, Module>)
|
||||
return ValueType::Module;
|
||||
|
||||
else if constexpr (std::is_same_v<T, InterfaceType>)
|
||||
return ValueType::InterfaceType;
|
||||
|
||||
else
|
||||
return ValueType::Any;
|
||||
},
|
||||
@@ -397,11 +410,11 @@ namespace Fig
|
||||
if (is<ValueType::StringClass>()) return FString(u8"<String \"") + as<ValueType::StringClass>() + FString(u8"\" >");
|
||||
if (is<ValueType::BoolClass>()) return as<ValueType::BoolClass>() ? FString(u8"true") : FString(u8"false");
|
||||
if (is<Function>())
|
||||
return FString(std::format("<Function {} at {:p}>",
|
||||
return FString(std::format("<Function '{}' at {:p}>",
|
||||
as<Function>().id,
|
||||
static_cast<const void *>(&as<Function>())));
|
||||
if (is<StructType>())
|
||||
return FString(std::format("<StructType {} at {:p}>",
|
||||
return FString(std::format("<StructType '{}' at {:p}>",
|
||||
as<StructType>().type.toString().toBasicString(),
|
||||
static_cast<const void *>(&as<StructType>())));
|
||||
if (is<StructInstance>())
|
||||
@@ -441,9 +454,17 @@ namespace Fig
|
||||
if (is<Module>())
|
||||
{
|
||||
return FString(std::format(
|
||||
"<Module at {:p}>",
|
||||
"<Module '{}' at {:p}>",
|
||||
as<Module>().name.toBasicString(),
|
||||
static_cast<const void *>(&as<Module>())));
|
||||
}
|
||||
if (is<InterfaceType>())
|
||||
{
|
||||
return FString(std::format(
|
||||
"<InterfaceType '{}' at {:p}",
|
||||
as<InterfaceType>().type.toString().toBasicString(),
|
||||
static_cast<const void *>(&as<InterfaceType>())));
|
||||
}
|
||||
return FString(u8"<error>");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user