PorygonLang/src/ScriptTypes/TableScriptType.hpp

197 lines
7.4 KiB
C++

#ifndef PORYGONLANG_TABLESCRIPTTYPE_HPP
#define PORYGONLANG_TABLESCRIPTTYPE_HPP
#include <unordered_map>
#include <utility>
#include <variant>
#include "../Binder/BoundVariables/BoundVariable.hpp"
#include "../Exception.hpp"
namespace Porygon{
class TableScriptType : public ScriptType{
public:
enum TableType{
Unknown,
Numerical,
StringKeyed,
Dictionary
};
private:
using ContentTypes = unordered_map<Utilities::HashedString, shared_ptr<const ScriptType>>*;
using KeyValueType = std::pair<shared_ptr<const ScriptType>, shared_ptr<const ScriptType>>;
bool _isContentAware = false;
TableType _tableType;
std::variant<
shared_ptr<const ScriptType>,
ContentTypes,
KeyValueType
> _valueType, _contentTypes, _keyValueType;
[[nodiscard]] inline shared_ptr<const ScriptType> GetValueType() const{
return std::get<shared_ptr<const ScriptType>>(_valueType);
}
[[nodiscard]] inline KeyValueType GetKeyValueTypes() const{
return std::get<KeyValueType>(_keyValueType);
}
public:
explicit TableScriptType()
: ScriptType(TypeClass::Table), _tableType(TableType::Unknown) {}
explicit TableScriptType(shared_ptr<const ScriptType> valueType)
: ScriptType(TypeClass::Table), _tableType(TableType::Numerical), _valueType(std::move(valueType)) {}
explicit TableScriptType(unordered_map<Utilities::HashedString, shared_ptr<const ScriptType>>* contentTypes)
: ScriptType(TypeClass::Table), _tableType(TableType::StringKeyed), _contentTypes(contentTypes),
_isContentAware(true) {}
explicit TableScriptType(std::pair<shared_ptr<ScriptType>, shared_ptr<ScriptType>> kvType)
: ScriptType(TypeClass::Table), _tableType(TableType::Dictionary), _keyValueType(std::move(kvType)) {}
~TableScriptType() override {
if (_tableType == TableType::StringKeyed)
delete GetContentTypes();
}
[[nodiscard]] bool CanBeIterated() const final {
return true;
}
[[nodiscard]] shared_ptr<const ScriptType> GetIteratorKeyType() const override {
switch (_tableType){
case Unknown: return ScriptType::AnyType;
case Numerical: return NumericScriptType::AwareInt;
case StringKeyed: return StringScriptType::Dynamic;
case Dictionary: throw Exception("Not implemented.");
}
}
[[nodiscard]] bool IsCountable() const override {
return true;
}
bool CanBeIndexedWith(const ScriptType *indexer) const override {
if (_tableType == TableType::Unknown)
return true;
else if (_tableType == TableType::StringKeyed){
return indexer->GetClass() == TypeClass::String;
}
else if (_tableType == TableType::Numerical){
return indexer->GetClass() == TypeClass::Number;
}
else{
auto keyType = GetKeyValueTypes().first;
return indexer->CastableTo(keyType, false) != CastResult ::InvalidCast;
}
}
[[nodiscard]] bool CanBeIndexedWithIdentifier(uint32_t hash) const override {
if (_tableType != TableType::StringKeyed)
return false;
return _isContentAware;
}
[[nodiscard]] shared_ptr<const ScriptType> GetIndexedType(shared_ptr<const ScriptType> indexer) const override {
if (_tableType == TableType::Unknown)
return ScriptType::AnyType;
else if (_tableType == TableType::StringKeyed){
auto stringType = dynamic_pointer_cast<const StringScriptType>(indexer);
if (stringType->IsKnownAtBind() && _isContentAware){
auto h = stringType->GetHashValue();
return GetContentTypes()->at(h);
}
return ScriptType::AnyType;
}
else if (_tableType == TableType::Numerical){
return GetValueType();
}
else{
return GetKeyValueTypes().second;
}
}
[[nodiscard]] shared_ptr<const ScriptType> GetIndexedType(uint32_t hash) const override {
auto lookup = Utilities::HashedString::CreateLookup(hash);
if (GetContentTypes()->find(lookup) != GetContentTypes()->end()){
return GetContentTypes()->at(lookup);
}
else{
return ScriptType::AnyType;
}
}
[[nodiscard]] inline ContentTypes GetContentTypes() const{
return std::get<ContentTypes>(_contentTypes);
}
[[nodiscard]]
bool CanSetIndexValue(shared_ptr<const ScriptType> indexer, shared_ptr<const ScriptType> val) const override {
switch (_tableType){
case Unknown: return true;
case Numerical:
return indexer->GetClass() == TypeClass ::Number &&
val->CastableTo(GetValueType(), false) != CastResult ::InvalidCast;
case StringKeyed:
return indexer->GetClass() == TypeClass ::String;
case Dictionary:
return indexer->CastableTo(GetKeyValueTypes().first, false) != CastResult ::InvalidCast &&
val->CastableTo(GetKeyValueTypes().second, false) != CastResult ::InvalidCast;
}
}
[[nodiscard]]
bool CanSetIndexValue(Utilities::HashedString indexer, shared_ptr<const ScriptType> val) const override {
switch (_tableType){
case StringKeyed:
return true;
case Unknown:
return true;
default:
break;
}
return false;
}
void SetIndexValue(shared_ptr<const ScriptType> indexer, shared_ptr<const ScriptType> val) const override {
if (_tableType == TableType::StringKeyed){
auto s = dynamic_pointer_cast<const StringScriptType>(indexer);
if (s->IsKnownAtBind()){
auto key = s->GetHashValue();
GetContentTypes()->insert({key, val});
}
}
else if (_tableType == TableType::Unknown){
auto t = const_cast<TableScriptType*>(this);
if (indexer->GetClass() == TypeClass::Number){
t->_tableType = TableType ::Numerical;
t->_valueType = val;
}
}
}
void SetIndexValue(Utilities::HashedString indexer, shared_ptr<const ScriptType> val) const override {
if (_tableType == TableType::StringKeyed){
GetContentTypes()->insert({indexer, val});
}
else if (_tableType == TableType::Unknown){
auto t = const_cast<TableScriptType*>(this);
t->_tableType = TableType ::StringKeyed;
t->_isContentAware = true;
t->_contentTypes = new unordered_map<Utilities::HashedString, shared_ptr<const ScriptType>>{
{indexer, val}
};
}
}
};
}
#include "ScriptType.hpp"
#endif //PORYGONLANG_TABLESCRIPTTYPE_HPP