This commit is contained in:
2026-08-02 15:14:43 +10:00
parent 16846d13bb
commit cf79fd00b9
3 changed files with 154 additions and 21 deletions

View File

@@ -17,9 +17,9 @@ namespace Solstice {
Add, Subtract, Multiply, Divide, Equal, NotEqual, GreaterThan, LesserThan
};
struct Type {
BaseType type = BaseType::None;
std::variant<
struct Type;
using TypeData = std::variant<
// object
std::map<std::string, Type>, // use map for ordering
// tuple
@@ -28,16 +28,20 @@ namespace Solstice {
std::shared_ptr<Type>,
// anything else
std::nullopt_t
> fields = std::nullopt;
>;
bool isConstant = false;
struct Type {
BaseType type = BaseType::None;
std::optional<std::map<std::string, Type>> getObject() const;
std::optional<std::vector<Type>> getTuple() const;
std::optional<Type> getArrayOrPointer() const;
TypeData fields = std::nullopt;
Type() = default;
Type(BaseType type) : type(type) {}
Type(BaseType type, TypeData fields) : type(type), fields(fields) {}
};
bool operator==(const Type& left, const Type& right);
@@ -99,6 +103,23 @@ namespace std {
return leftHash;
}
};
template<>
struct hash<vector<Solstice::Type>> {
size_t operator()(const vector<Solstice::Type> v) const {
size_t hash = 0;
for (const auto& type : v) {
size_t typeHash = std::hash<Solstice::Type>{}(type);
if (hash == 0) {
hash = typeHash;
} else {
hash ^= typeHash + 0x9e3779b97f4a7c15ULL + (hash << 6) + (hash >> 2);
}
}
return hash;
}
};
}
namespace Solstice {
@@ -106,6 +127,9 @@ namespace Solstice {
struct PossibleType {
std::unordered_set<Type> possiblities;
bool unknownType = false;
bool isConstant = false;
PossibleType() = default;
PossibleType(const std::unordered_set<Type>& possiblities) : possiblities(possiblities) {}
@@ -113,13 +137,9 @@ namespace Solstice {
};
struct Function {
PossibleType returnType = {{BaseType::None}};
std::vector<PossibleType> argumentTypes = {};
std::unordered_map<std::vector<Type>, Type> signatureToReturnType;
Function() = default;
Function(PossibleType returnType) : returnType(returnType) {}
Function(PossibleType returnType, std::vector<PossibleType> argumentTypes)
: returnType(returnType), argumentTypes(argumentTypes) {}
};
}

View File

@@ -6,6 +6,20 @@
namespace Solstice {
void TypeChecker::initOverloads() {
types = {
{"Int", {BaseType::Int}},
{"String", {BaseType::String}},
{"Double", {BaseType::Double}},
{"Bool", {BaseType::Bool}},
{"Char", {BaseType::Char}},
{"None", {}},
{"Tuple", {BaseType::Tuple}},
{"Array", {BaseType::Array}},
{"Pointer", {BaseType::Tuple}},
{"Object", {BaseType::Tuple}},
};
addOverloads = {
{{BaseType::Int, BaseType::Int}, BaseType::Int},
{{BaseType::Double, BaseType::Double}, BaseType::Double},
@@ -132,7 +146,7 @@ namespace Solstice {
if (!nodeType.has_value()) {
throw std::runtime_error("cannot assign ambiguous type to name");
}
variables[*id] = *nodeType;
variables[*id] = {{*nodeType}};
variables[*id].isConstant = true;
}
@@ -145,12 +159,44 @@ namespace Solstice {
throw std::runtime_error("Cannot overwrite existing bind/variable/function with new function");
}
checkNodeType(node.children[1]);
auto nodeType = node.children[1].ptype.getOnlyType();
// create sub-checker
TypeChecker typeChecker(*this);
typeChecker.setNode(node.children[2]);
for (const auto& arg : node.children[1].children) {
switch (arg.type) {
case NodeType::Identifier: {
typeChecker.setVariableUnknown(*arg.getIdentifier());
break;
}
case NodeType::SetType: {
// type annotation provided
if (arg.children[0].type != NodeType::Identifier && arg.children[1].type != NodeType::Identifier) {
throw std::runtime_error("Invalid function parameter definition");
}
auto typeId = arg.children[1].getIdentifier();
if (!typeId.has_value()) {
throw std::runtime_error("FIXME identifier node does not contain identifier");
}
if (types.find(*typeId) == types.end()) {
throw std::runtime_error("unknown type " + *typeId);
}
typeChecker.setVariable(*arg.children[0].getIdentifier(), types[*typeId]);
break;
}
default: {
throw std::runtime_error("Invalid function parameter definition");
}
}
}
typeChecker.checkTypes();
auto nodeType = node.children[2].ptype.getOnlyType();
if (!nodeType.has_value()) {
throw std::runtime_error("cannot assign ambiguous type to name");
}
variables[*id] = *nodeType;
variables[*id] = {{*nodeType}};
variables[*id].isConstant = true;
}
@@ -173,10 +219,21 @@ namespace Solstice {
if (!nodeType.has_value()) {
throw std::runtime_error("cannot assign ambiguous type to name");
}
variables[*id] = *nodeType;
variables[*id] = {{*nodeType}};
}
void TypeChecker::checkCodeBlockType(Node& node) {}
void TypeChecker::checkCodeBlockType(Node& node) {
return;
}
void TypeChecker::checkAddType(Node& node) {
// check node children
checkNodeType(node.children[0]);
checkNodeType(node.children[1]);
}
void TypeChecker::checkNodeType(Node& node) {
@@ -184,7 +241,16 @@ namespace Solstice {
case NodeType::Root:
node.ptype = {{}}; // no possible types for root node
for (auto& child : node.children) {
// only allow certain node types in the root
switch (child.type) {
case NodeType::Bind:
case NodeType::FunctionBind:
checkNodeType(child);
break;
default:
throw std::runtime_error("Illegal node type in file root");
}
}
break;
case NodeType::Literal:
@@ -211,12 +277,36 @@ namespace Solstice {
case NodeType::CodeBlock:
checkCodeBlockType(node);
break;
case NodeType::Add:
checkAddType(node);
break;
case NodeType::Subtract:
checkSubtractType(node);
break;
case NodeType::Multiply:
checkMultiplyType(node);
break;
case NodeType::Divide:
checkDivideType(node);
break;
case NodeType::Equal:
checkEqualType(node);
break;
case NodeType::NotEqual:
checkNotEqualType(node);
break;
case NodeType::GreaterThan:
checkGreaterThanType(node);
break;
case NodeType::LesserThan:
checkLesserThanType(node);
break;
}
}
void TypeChecker::checkTypes() {
checkNodeType(input);
checkNodeType(*input);
}

View File

@@ -9,7 +9,7 @@
namespace Solstice {
class TypeChecker {
std::unordered_map<std::string, Type> variables;
std::unordered_map<std::string, PossibleType> variables;
std::unordered_map<std::string, Type> types;
std::unordered_map<TypePair, Type> addOverloads;
@@ -21,7 +21,7 @@ namespace Solstice {
std::unordered_map<TypePair, Type> greaterThanOverloads;
std::unordered_map<TypePair, Type> lesserThanOverloads;
Node& input;
Node* input;
bool inFunction = false;
@@ -37,15 +37,38 @@ namespace Solstice {
void checkCodeBlockType(Node& node);
void checkAddType(Node& node);
void checkSubtractType(Node& node);
void checkMultiplyType(Node& node);
void checkDivideType(Node& node);
void checkEqualType(Node& node);
void checkNotEqualType(Node& node);
void checkGreaterThanType(Node& node);
void checkLesserThanType(Node& node);
void checkNodeType(Node& node);
public:
TypeChecker() = delete;
TypeChecker(Node& node) : input(node) {
TypeChecker(Node& node) : input(&node) {
initOverloads();
}
void setNode(Node& node) {
input = &node;
}
// Only for use as a public function.
void setVariable(const std::string& name, const Type& type) {
variables[name] = {{type}};
}
void setVariableUnknown(const std::string& name) {
variables[name].unknownType = true;
}
void checkTypes();
};
}