[VER] 0.3.3-alpha

[FEAT] interface + impl 支持! Duck Typing + 严格的检查让语言健壮
[FEAT][IMPL] 增加辅助函数 isTypeMatch等
[IMPL] TypeInfo构造函数FString 现在 explicit
This commit is contained in:
2025-12-30 12:02:27 +08:00
parent f19b14f45f
commit f58a60c8e3
15 changed files with 852 additions and 80 deletions

View File

@@ -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>;
};

View 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>;
};

View File

@@ -21,6 +21,7 @@
#include <Ast/Statements/IfSt.hpp> #include <Ast/Statements/IfSt.hpp>
#include <Ast/Statements/ImplementSt.hpp> #include <Ast/Statements/ImplementSt.hpp>
#include <Ast/Statements/ImportSt.hpp> #include <Ast/Statements/ImportSt.hpp>
#include <Ast/Statements/InterfaceDefSt.hpp>
#include <Ast/Statements/FunctionDefSt.hpp> #include <Ast/Statements/FunctionDefSt.hpp>
#include <Ast/Statements/ControlSt.hpp> #include <Ast/Statements/ControlSt.hpp>
#include <Ast/Statements/ExpressionStmt.hpp> #include <Ast/Statements/ExpressionStmt.hpp>

View File

@@ -44,6 +44,7 @@ namespace Fig::Ast
VarDefSt, VarDefSt,
FunctionDefSt, FunctionDefSt,
StructSt, StructSt,
InterfaceDefSt,
ImplementSt, ImplementSt,
IfSt, IfSt,

View File

@@ -4,6 +4,8 @@
#include <Value/Type.hpp> #include <Value/Type.hpp>
#include <Core/fig_string.hpp> #include <Core/fig_string.hpp>
#include <format>
namespace Fig::Ast namespace Fig::Ast
{ {
struct FunctionParameters // for define struct FunctionParameters // for define
@@ -26,7 +28,6 @@ namespace Fig::Ast
FunctionParameters() FunctionParameters()
{ {
} }
FunctionParameters(PosParasType _posParas, DefParasType _defParas) FunctionParameters(PosParasType _posParas, DefParasType _defParas)
{ {
@@ -43,5 +44,52 @@ namespace Fig::Ast
{ {
return posParas.size() + defParas.size(); 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

View File

@@ -1,5 +1,8 @@
#pragma once #pragma once
#include "Value/interface.hpp"
#include <Value/Type.hpp>
#include <algorithm>
#include <unordered_map> #include <unordered_map>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
@@ -12,6 +15,14 @@
namespace Fig namespace Fig
{ {
struct ImplRecord
{
TypeInfo interfaceType;
TypeInfo structType;
std::unordered_map<FString, Function> implMethods;
};
class Context : public std::enable_shared_from_this<Context> class Context : public std::enable_shared_from_this<Context>
{ {
private: private:
@@ -20,7 +31,9 @@ namespace Fig
std::unordered_map<std::size_t, Function> functions; std::unordered_map<std::size_t, Function> functions;
std::unordered_map<std::size_t, FString> functionNames; 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: public:
ContextPtr parent; ContextPtr parent;
@@ -28,7 +41,7 @@ namespace Fig
Context(const Context &) = default; Context(const Context &) = default;
Context(const FString &name, ContextPtr p = nullptr) : Context(const FString &name, ContextPtr p = nullptr) :
scopeName(name), parent(p) {} scopeName(name), parent(p) {}
void setParent(ContextPtr _parent) void setParent(ContextPtr _parent)
{ {
parent = _parent; parent = _parent;
@@ -49,6 +62,7 @@ namespace Fig
variables.insert(c.variables.begin(), c.variables.end()); variables.insert(c.variables.begin(), c.variables.end());
functions.insert(c.functions.begin(), c.functions.end()); functions.insert(c.functions.begin(), c.functions.end());
functionNames.insert(c.functionNames.begin(), c.functionNames.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()); // structTypeNames.insert(c.structTypeNames.begin(), c.structTypeNames.end());
} }
@@ -243,6 +257,169 @@ namespace Fig
} }
return false; 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 void printStackTrace(std::ostream &os = std::cerr, int indent = 0) const
{ {
const Context *ctx = this; const Context *ctx = this;

View File

@@ -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 <Error/errorLog.hpp>
#include <Evaluator/evaluator.hpp> #include <Evaluator/evaluator.hpp>
#include <Evaluator/evaluator_error.hpp> #include <Evaluator/evaluator_error.hpp>
@@ -8,21 +19,14 @@
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <memory>
namespace Fig 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 implMethod.name == ifMethod.name && implMethod.paras == ifMethod.paras;
return true;
TypeInfo actual = obj->getTypeInfo();
if (actual != ValueType::StructInstance)
return expected == actual;
const StructInstance &si = obj->as<StructInstance>();
return si.parentType == expected;
} }
LvObject Evaluator::evalVarExpr(Ast::VarExpr var, ContextPtr ctx) LvObject Evaluator::evalVarExpr(Ast::VarExpr var, ContextPtr ctx)
@@ -32,7 +36,7 @@ namespace Fig
{ {
throw EvaluatorError(u8"UndeclaredIdentifierError", name, var); 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) LvObject Evaluator::evalMemberExpr(Ast::MemberExpr me, ContextPtr ctx)
{ {
@@ -45,7 +49,8 @@ namespace Fig
if (mod.ctx->contains(member) && mod.ctx->isVariablePublic(member)) if (mod.ctx->contains(member) && mod.ctx->isVariablePublic(member))
{ {
return LvObject( return LvObject(
mod.ctx->get(member)); mod.ctx->get(member),
ctx);
} }
else else
{ {
@@ -61,13 +66,14 @@ namespace Fig
if (baseVal->hasMemberFunction(member)) if (baseVal->hasMemberFunction(member))
{ {
return LvObject(std::make_shared<VariableSlot>( return LvObject(std::make_shared<VariableSlot>(
member, member,
std::make_shared<Object>( std::make_shared<Object>(
Function( Function(
baseVal->getMemberFunction(member), baseVal->getMemberFunction(member),
baseVal->getMemberFunctionParaCount(member))), baseVal->getMemberFunctionParaCount(member))),
ValueType::Function, ValueType::Function,
AccessModifier::PublicConst)); // fake l-value AccessModifier::PublicConst),
ctx); // fake l-value
} }
if (baseVal->getTypeInfo() != ValueType::StructInstance) // and not member function found if (baseVal->getTypeInfo() != ValueType::StructInstance) // and not member function found
{ {
@@ -80,17 +86,40 @@ namespace Fig
me->base); me->base);
} }
const StructInstance &si = baseVal->as<StructInstance>(); 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( throw EvaluatorError(
u8"NoAttributeError", u8"NoAttributeError",
std::format( std::format(
"`{}` has not attribute '{}'", "`{}` has not attribute '{}' and no interfaces have been implemented it",
baseVal->toString().toBasicString(), baseVal->toString().toBasicString(),
member.toBasicString()), member.toBasicString()),
me->base); me->base);
} }
return LvObject(si.localContext->get(member));
} }
LvObject Evaluator::evalIndexExpr(Ast::IndexExpr ie, ContextPtr ctx) LvObject Evaluator::evalIndexExpr(Ast::IndexExpr ie, ContextPtr ctx)
{ {
@@ -125,14 +154,16 @@ namespace Fig
return LvObject( return LvObject(
base.get(), base.get(),
indexVal, indexVal,
LvObject::Kind::ListElement); LvObject::Kind::ListElement,
ctx);
} }
else if (type == ValueType::Map) else if (type == ValueType::Map)
{ {
return LvObject( return LvObject(
base.get(), base.get(),
index, index,
LvObject::Kind::MapElement); LvObject::Kind::MapElement,
ctx);
} }
else if (type == ValueType::String) else if (type == ValueType::String)
{ {
@@ -160,7 +191,8 @@ namespace Fig
return LvObject( return LvObject(
base.get(), base.get(),
indexVal, indexVal,
LvObject::Kind::StringElement); LvObject::Kind::StringElement,
ctx);
} }
else 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 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); ObjectPtr argVal = eval(fnArgs.argv[i], ctx);
TypeInfo actualType = argVal->getTypeInfo(); TypeInfo actualType = argVal->getTypeInfo();
if (!isTypeMatch(expectedType, argVal)) if (!isTypeMatch(expectedType, argVal, ctx))
{ {
throw EvaluatorError( throw EvaluatorError(
u8"ArgumentTypeMismatchError", u8"ArgumentTypeMismatchError",
@@ -467,10 +499,10 @@ namespace Fig
for (; i < fnArgs.getLength(); i++) for (; i < fnArgs.getLength(); i++)
{ {
size_t defParamIndex = i - fnParas.posParas.size(); 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); ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, ctx);
if (!isTypeMatch(expectedType, defaultVal)) if (!isTypeMatch(expectedType, defaultVal, ctx))
{ {
throw EvaluatorError( throw EvaluatorError(
u8"DefaultParameterTypeError", u8"DefaultParameterTypeError",
@@ -484,7 +516,7 @@ namespace Fig
ObjectPtr argVal = eval(fnArgs.argv[i], ctx); ObjectPtr argVal = eval(fnArgs.argv[i], ctx);
TypeInfo actualType = argVal->getTypeInfo(); TypeInfo actualType = argVal->getTypeInfo();
if (!isTypeMatch(expectedType, argVal)) if (!isTypeMatch(expectedType, argVal, ctx))
{ {
throw EvaluatorError( throw EvaluatorError(
u8"ArgumentTypeMismatchError", u8"ArgumentTypeMismatchError",
@@ -512,13 +544,13 @@ namespace Fig
if (j < fnParas.posParas.size()) if (j < fnParas.posParas.size())
{ {
paramName = fnParas.posParas[j].first; paramName = fnParas.posParas[j].first;
paramType = fnParas.posParas[j].second; paramType = TypeInfo(fnParas.posParas[j].second);
} }
else else
{ {
size_t defParamIndex = j - fnParas.posParas.size(); size_t defParamIndex = j - fnParas.posParas.size();
paramName = fnParas.defParas[defParamIndex].first; paramName = fnParas.defParas[defParamIndex].first;
paramType = fnParas.defParas[defParamIndex].second.first; paramType = TypeInfo(fnParas.defParas[defParamIndex].second.first);
} }
AccessModifier argAm = AccessModifier::Const; AccessModifier argAm = AccessModifier::Const;
newContext->def(paramName, paramType, argAm, evaluatedArgs.argv[j]); newContext->def(paramName, paramType, argAm, evaluatedArgs.argv[j]);
@@ -553,7 +585,7 @@ namespace Fig
break; break;
} }
} }
if (!isTypeMatch(fnStruct.retType, retVal)) if (!isTypeMatch(fnStruct.retType, retVal, ctx))
{ {
throw EvaluatorError( throw EvaluatorError(
u8"ReturnTypeMismatchError", u8"ReturnTypeMismatchError",
@@ -741,7 +773,7 @@ namespace Fig
ObjectPtr defaultVal = eval(field.defaultValue, ctx); // it can't be null here ObjectPtr defaultVal = eval(field.defaultValue, ctx); // it can't be null here
// type check // type check
if (!isTypeMatch(expectedType, defaultVal)) if (!isTypeMatch(expectedType, defaultVal, ctx))
{ {
throw EvaluatorError( throw EvaluatorError(
u8"StructFieldTypeMismatchError", u8"StructFieldTypeMismatchError",
@@ -759,7 +791,7 @@ namespace Fig
} }
const ObjectPtr &argVal = evaluatedArgs[i].second; const ObjectPtr &argVal = evaluatedArgs[i].second;
if (!isTypeMatch(expectedType, argVal)) if (!isTypeMatch(expectedType, argVal, ctx))
{ {
throw EvaluatorError( throw EvaluatorError(
u8"StructFieldTypeMismatchError", u8"StructFieldTypeMismatchError",
@@ -797,7 +829,7 @@ namespace Fig
// type check // type check
const TypeInfo &expectedType = field.type; const TypeInfo &expectedType = field.type;
if (!isTypeMatch(expectedType, defaultVal)) if (!isTypeMatch(expectedType, defaultVal, ctx))
{ {
throw EvaluatorError( throw EvaluatorError(
u8"StructFieldTypeMismatchError", u8"StructFieldTypeMismatchError",
@@ -814,7 +846,7 @@ namespace Fig
continue; continue;
} }
const ObjectPtr &argVal = evaluatedArgs[i].second; const ObjectPtr &argVal = evaluatedArgs[i].second;
if (!isTypeMatch(field.type, argVal)) if (!isTypeMatch(field.type, argVal, ctx))
{ {
throw EvaluatorError( throw EvaluatorError(
u8"StructFieldTypeMismatchError", u8"StructFieldTypeMismatchError",
@@ -918,7 +950,7 @@ namespace Fig
else if (!declaredTypeName.empty()) else if (!declaredTypeName.empty())
{ {
declaredType = TypeInfo(declaredTypeName); declaredType = TypeInfo(declaredTypeName);
if (value != nullptr && !isTypeMatch(declaredType, value)) if (value != nullptr && !isTypeMatch(declaredType, value, ctx))
{ {
throw EvaluatorError( throw EvaluatorError(
u8"TypeError", u8"TypeError",
@@ -1031,6 +1063,194 @@ namespace Fig
return StatementResult::normal(); 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: { case IfSt: {
auto ifSt = std::dynamic_pointer_cast<Ast::IfSt>(stmt); auto ifSt = std::dynamic_pointer_cast<Ast::IfSt>(stmt);
ObjectPtr condVal = eval(ifSt->condition, ctx); ObjectPtr condVal = eval(ifSt->condition, ctx);

View File

@@ -1,3 +1,6 @@
#include "Ast/Statements/ImplementSt.hpp"
#include "Ast/Statements/InterfaceDefSt.hpp"
#include "Value/Type.hpp"
#include <Ast/ast.hpp> #include <Ast/ast.hpp>
#include <Context/context.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*/ /* Left-value eval*/
LvObject evalVarExpr(Ast::VarExpr, ContextPtr); LvObject evalVarExpr(Ast::VarExpr, ContextPtr);

View File

@@ -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> #include <Parser/parser.hpp>
namespace Fig namespace Fig
@@ -365,6 +370,114 @@ namespace Fig
} }
return makeAst<Ast::StructDefSt>(isPublic, structName, fields, makeAst<Ast::BlockStatementAst>(stmts)); 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 Parser::__parseStatement()
{ {
Ast::Statement stmt; Ast::Statement stmt;
@@ -389,9 +502,13 @@ namespace Fig
{ {
stmt = __parseStructDef(true); stmt = __parseStructDef(true);
} }
else if (isThis(TokenType::Interface))
{
stmt = __parseInterfaceDef(true);
}
else 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)) else if (isThis(TokenType::Variable) || isThis(TokenType::Const))
@@ -409,6 +526,16 @@ namespace Fig
next(); next();
stmt = __parseStructDef(false); 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)) else if (isThis(TokenType::If))
{ {
stmt = __parseIf(); stmt = __parseIf();
@@ -501,7 +628,7 @@ namespace Fig
expect(TokenType::RightParen); expect(TokenType::RightParen);
next(); // consume `)` next(); // consume `)`
} }
else else
{ {
elifCondition = parseExpression(0); elifCondition = parseExpression(0);
} }

View File

@@ -313,6 +313,8 @@ namespace Fig
Ast::VarExpr __parseVarExpr(FString); Ast::VarExpr __parseVarExpr(FString);
Ast::FunctionDef __parseFunctionDef(bool); // entry: current is Token::Identifier (isPublic: Bool) 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::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::BinaryExpr __parseInfix(Ast::Expression, Ast::Operator, Precedence);
Ast::UnaryExpr __parsePrefix(Ast::Operator, Precedence); Ast::UnaryExpr __parsePrefix(Ast::Operator, Precedence);

View File

@@ -22,18 +22,20 @@ namespace Fig
ObjectPtr mapIndex; ObjectPtr mapIndex;
LvObject(std::shared_ptr<VariableSlot> _slot) : ContextPtr ctx;
slot(std::move(_slot))
LvObject(std::shared_ptr<VariableSlot> _slot, ContextPtr _ctx) :
slot(std::move(_slot)), ctx(_ctx)
{ {
kind = Kind::Variable; kind = Kind::Variable;
} }
LvObject(ObjectPtr _v, size_t _index, Kind _kind) : LvObject(ObjectPtr _v, size_t _index, Kind _kind, ContextPtr _ctx) :
value(_v), numIndex(_index) value(_v), numIndex(_index), ctx(_ctx)
{ {
kind = _kind; kind = _kind;
} }
LvObject(ObjectPtr _v, ObjectPtr _index, Kind _kind) : LvObject(ObjectPtr _v, ObjectPtr _index, Kind _kind, ContextPtr _ctx) :
value(_v), mapIndex(_index) value(_v), mapIndex(_index), ctx(_ctx)
{ {
kind = _kind; kind = _kind;
} }
@@ -78,7 +80,7 @@ namespace Fig
if (kind == Kind::Variable) if (kind == Kind::Variable)
{ {
auto s = resolve(slot); auto s = resolve(slot);
if (s->declaredType != ValueType::Any && s->declaredType != v->getTypeInfo()) if (!isTypeMatch(s->declaredType, v, ctx))
{ {
throw RuntimeError( throw RuntimeError(
FString( FString(

View File

@@ -35,7 +35,7 @@ namespace Fig
} }
TypeInfo(); TypeInfo();
TypeInfo(FString _name, bool reg = false); explicit TypeInfo(const FString &_name, bool reg = false);
TypeInfo(const TypeInfo &other) = default; TypeInfo(const TypeInfo &other) = default;
bool operator==(const TypeInfo &other) const bool operator==(const TypeInfo &other) const
@@ -68,7 +68,7 @@ namespace Fig
extern const TypeInfo List; extern const TypeInfo List;
extern const TypeInfo Map; extern const TypeInfo Map;
extern const TypeInfo Module; extern const TypeInfo Module;
// extern const TypeInfo Tuple; extern const TypeInfo InterfaceType;
using IntClass = int64_t; using IntClass = int64_t;
using DoubleClass = double; using DoubleClass = double;

20
src/Value/interface.hpp Normal file
View 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)
}
};
}

View File

@@ -1,3 +1,4 @@
#include "Value/Type.hpp"
#include <Value/value.hpp> #include <Value/value.hpp>
#include <Context/context.hpp> #include <Context/context.hpp>
@@ -7,12 +8,14 @@ namespace Fig
{ {
std::map<FString, size_t> TypeInfo::typeMap = {}; std::map<FString, size_t> TypeInfo::typeMap = {};
TypeInfo::TypeInfo() : // only allow use in evaluate time !! <---- dynamic type system requirement TypeInfo::TypeInfo() : // only allow use in evaluate time !! <---- dynamic type system requirement
id(1), name(FString(u8"Any")) {} id(1), name(FString(u8"Any"))
TypeInfo::TypeInfo(FString _name, bool reg) {
}
TypeInfo::TypeInfo(const FString &_name, bool reg)
{ {
static size_t id_count = 0; static size_t id_count = 0;
name = std::move(_name); name = _name;
// std::cerr << "TypeInfo constructor called for type name: " << name.toBasicString() << "\n"; // std::cerr << "TypeInfo constructor called for type name: " << name.toBasicString() << "\n";
if (reg) if (reg)
{ {
@@ -21,10 +24,17 @@ namespace Fig
} }
else else
{ {
if (!typeMap.contains(_name))
{
throw RuntimeError(FString(std::format(
"No type named '{}'",
_name.toBasicString())));
// *this = ValueType::String;
}
id = typeMap.at(name); // may throw id = typeMap.at(name); // may throw
} }
} }
size_t ValueKeyHash::operator()(const ValueKey &key) const size_t ValueKeyHash::operator()(const ValueKey &key) const
{ {
{ {
@@ -69,7 +79,7 @@ namespace Fig
} }
} }
FString prettyType(ObjectPtr obj) FString prettyType(std::shared_ptr<const Object> obj)
{ {
auto t = obj->getTypeInfo(); auto t = obj->getTypeInfo();
if (t == ValueType::StructInstance) if (t == ValueType::StructInstance)
@@ -77,16 +87,51 @@ namespace Fig
return t.toString(); return t.toString();
} }
const TypeInfo ValueType::Any(FString(u8"Any"), true); // id: 1 const TypeInfo ValueType::Any(FString(u8"Any"), true); // id: 1
const TypeInfo ValueType::Null(FString(u8"Null"), true); // id: 2 const TypeInfo ValueType::Null(FString(u8"Null"), true); // id: 2
const TypeInfo ValueType::Int(FString(u8"Int"), true); // id: 3 const TypeInfo ValueType::Int(FString(u8"Int"), true); // id: 3
const TypeInfo ValueType::String(FString(u8"String"), true); // id: 4 const TypeInfo ValueType::String(FString(u8"String"), true); // id: 4
const TypeInfo ValueType::Bool(FString(u8"Bool"), true); // id: 5 const TypeInfo ValueType::Bool(FString(u8"Bool"), true); // id: 5
const TypeInfo ValueType::Double(FString(u8"Double"), true); // id: 6 const TypeInfo ValueType::Double(FString(u8"Double"), true); // id: 6
const TypeInfo ValueType::Function(FString(u8"Function"), true); // id: 7 const TypeInfo ValueType::Function(FString(u8"Function"), true); // id: 7
const TypeInfo ValueType::StructType(FString(u8"StructType"), true); // id: 8 const TypeInfo ValueType::StructType(FString(u8"StructType"), true); // id: 8
const TypeInfo ValueType::StructInstance(FString(u8"StructInstance"), true); // id: 9 const TypeInfo ValueType::StructInstance(FString(u8"StructInstance"), true); // id: 9
const TypeInfo ValueType::List(FString(u8"List"), true); // id: 10 const TypeInfo ValueType::List(FString(u8"List"), true); // id: 10
const TypeInfo ValueType::Map(FString(u8"Map"), true); // id: 11 const TypeInfo ValueType::Map(FString(u8"Map"), true); // id: 11
const TypeInfo ValueType::Module(FString(u8"Module"), true); // id: 12 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 } // namespace Fig

View File

@@ -1,11 +1,13 @@
#pragma once #pragma once
#include <Value/function.hpp> #include <Value/function.hpp>
#include <Value/interface.hpp>
#include <Value/structType.hpp> #include <Value/structType.hpp>
#include <Value/structInstance.hpp> #include <Value/structInstance.hpp>
#include <Value/Type.hpp> #include <Value/Type.hpp>
#include <Value/valueError.hpp> #include <Value/valueError.hpp>
#include <Value/module.hpp> #include <Value/module.hpp>
#include <memory>
#include <variant> #include <variant>
#include <cmath> #include <cmath>
#include <string> #include <string>
@@ -33,7 +35,7 @@ namespace Fig
using ObjectPtr = std::shared_ptr<Object>; using ObjectPtr = std::shared_ptr<Object>;
using List = std::vector<ObjectPtr>; using List = std::vector<ObjectPtr>;
FString prettyType(ObjectPtr obj); FString prettyType(std::shared_ptr<const Object> obj);
struct ValueKey struct ValueKey
{ {
@@ -48,7 +50,10 @@ namespace Fig
}; };
using Map = std::unordered_map<ValueKey, ObjectPtr, ValueKeyHash>; 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: public:
using VariantType = std::variant< using VariantType = std::variant<
@@ -62,7 +67,8 @@ namespace Fig
StructInstance, StructInstance,
List, List,
Map, Map,
Module>; Module,
InterfaceType>;
std::unordered_map<TypeInfo, std::unordered_map<TypeInfo,
std::unordered_map<FString, std::unordered_map<FString,
@@ -207,7 +213,9 @@ namespace Fig
map.contains(index)); map.contains(index));
}}, }},
}}, }},
{ValueType::Module, {}}}; {ValueType::Module, {}},
{ValueType::InterfaceType, {}},
};
std::unordered_map<TypeInfo, std::unordered_map<FString, int>, TypeInfoHash> memberTypeFunctionsParas{ std::unordered_map<TypeInfo, std::unordered_map<FString, int>, TypeInfoHash> memberTypeFunctionsParas{
{ValueType::Null, {}}, {ValueType::Null, {}},
{ValueType::Int, {}}, {ValueType::Int, {}},
@@ -226,8 +234,8 @@ namespace Fig
{u8"get", 1}, {u8"get", 1},
{u8"contains", 1}, {u8"contains", 1},
}}, }},
{ValueType::Module, {}} {ValueType::Module, {}},
{ValueType::InterfaceType, {}},
}; };
bool hasMemberFunction(const FString &name) const bool hasMemberFunction(const FString &name) const
{ {
@@ -268,6 +276,8 @@ namespace Fig
data(m) {} data(m) {}
Object(const Module &m) : Object(const Module &m) :
data(m) {} data(m) {}
Object(const InterfaceType &i) :
data(i) {}
Object(const Object &) = default; Object(const Object &) = default;
Object(Object &&) noexcept = default; Object(Object &&) noexcept = default;
@@ -364,6 +374,9 @@ namespace Fig
else if constexpr (std::is_same_v<T, Module>) else if constexpr (std::is_same_v<T, Module>)
return ValueType::Module; return ValueType::Module;
else if constexpr (std::is_same_v<T, InterfaceType>)
return ValueType::InterfaceType;
else else
return ValueType::Any; 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::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<ValueType::BoolClass>()) return as<ValueType::BoolClass>() ? FString(u8"true") : FString(u8"false");
if (is<Function>()) if (is<Function>())
return FString(std::format("<Function {} at {:p}>", return FString(std::format("<Function '{}' at {:p}>",
as<Function>().id, as<Function>().id,
static_cast<const void *>(&as<Function>()))); static_cast<const void *>(&as<Function>())));
if (is<StructType>()) if (is<StructType>())
return FString(std::format("<StructType {} at {:p}>", return FString(std::format("<StructType '{}' at {:p}>",
as<StructType>().type.toString().toBasicString(), as<StructType>().type.toString().toBasicString(),
static_cast<const void *>(&as<StructType>()))); static_cast<const void *>(&as<StructType>())));
if (is<StructInstance>()) if (is<StructInstance>())
@@ -441,9 +454,17 @@ namespace Fig
if (is<Module>()) if (is<Module>())
{ {
return FString(std::format( return FString(std::format(
"<Module at {:p}>", "<Module '{}' at {:p}>",
as<Module>().name.toBasicString(),
static_cast<const void *>(&as<Module>()))); 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>"); return FString(u8"<error>");
} }