2026-08-01 22:12:05 +10:00
|
|
|
#include "typechecker.hpp"
|
2026-08-02 08:59:18 +10:00
|
|
|
#include "type.hpp"
|
|
|
|
|
#include <stdexcept>
|
2026-08-02 14:09:38 +10:00
|
|
|
#include <iostream>
|
2026-08-01 22:12:05 +10:00
|
|
|
|
|
|
|
|
namespace Solstice {
|
|
|
|
|
|
|
|
|
|
void TypeChecker::initOverloads() {
|
|
|
|
|
addOverloads = {
|
|
|
|
|
{{BaseType::Int, BaseType::Int}, BaseType::Int},
|
|
|
|
|
{{BaseType::Double, BaseType::Double}, BaseType::Double},
|
|
|
|
|
{{BaseType::Int, BaseType::Double}, BaseType::Double},
|
|
|
|
|
{{BaseType::Double, BaseType::Int}, BaseType::Double},
|
|
|
|
|
{{BaseType::String, BaseType::String}, BaseType::String},
|
|
|
|
|
{{BaseType::String, BaseType::Char}, BaseType::String},
|
|
|
|
|
{{BaseType::Char, BaseType::String}, BaseType::String},
|
|
|
|
|
{{BaseType::Char, BaseType::Char}, BaseType::Char}
|
|
|
|
|
};
|
|
|
|
|
subtractOverloads = {
|
|
|
|
|
{{BaseType::Int, BaseType::Int}, BaseType::Int},
|
|
|
|
|
{{BaseType::Double, BaseType::Double}, BaseType::Double},
|
|
|
|
|
{{BaseType::Int, BaseType::Double}, BaseType::Double},
|
|
|
|
|
{{BaseType::Double, BaseType::Int}, BaseType::Double},
|
|
|
|
|
{{BaseType::Char, BaseType::Char}, BaseType::Char}
|
|
|
|
|
};
|
|
|
|
|
multiplyOverloads = {
|
|
|
|
|
{{BaseType::Int, BaseType::Int}, BaseType::Int},
|
|
|
|
|
{{BaseType::Double, BaseType::Double}, BaseType::Double},
|
|
|
|
|
{{BaseType::Int, BaseType::Double}, BaseType::Double},
|
|
|
|
|
{{BaseType::Double, BaseType::Int}, BaseType::Double},
|
|
|
|
|
{{BaseType::Char, BaseType::Char}, BaseType::Char}
|
|
|
|
|
};
|
|
|
|
|
divideOverloads = {
|
|
|
|
|
{{BaseType::Int, BaseType::Int}, BaseType::Double},
|
|
|
|
|
{{BaseType::Double, BaseType::Double}, BaseType::Double},
|
|
|
|
|
{{BaseType::Int, BaseType::Double}, BaseType::Double},
|
|
|
|
|
{{BaseType::Double, BaseType::Int}, BaseType::Double},
|
|
|
|
|
{{BaseType::Char, BaseType::Char}, BaseType::Char}
|
|
|
|
|
};
|
|
|
|
|
equalOverloads = {
|
|
|
|
|
{{BaseType::Int, BaseType::Int}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Double, BaseType::Double}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Int, BaseType::Double}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Double, BaseType::Int}, BaseType::Bool},
|
|
|
|
|
{{BaseType::String, BaseType::String}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Char, BaseType::Char}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Bool, BaseType::Bool}, BaseType::Bool},
|
|
|
|
|
};
|
|
|
|
|
notEqualOverloads = {
|
|
|
|
|
{{BaseType::Int, BaseType::Int}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Double, BaseType::Double}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Int, BaseType::Double}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Double, BaseType::Int}, BaseType::Bool},
|
|
|
|
|
{{BaseType::String, BaseType::String}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Char, BaseType::Char}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Bool, BaseType::Bool}, BaseType::Bool},
|
|
|
|
|
};
|
|
|
|
|
greaterThanOverloads = {
|
|
|
|
|
{{BaseType::Int, BaseType::Int}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Double, BaseType::Double}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Int, BaseType::Double}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Double, BaseType::Int}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Char, BaseType::Char}, BaseType::Bool},
|
|
|
|
|
};
|
|
|
|
|
lesserThanOverloads = {
|
|
|
|
|
{{BaseType::Int, BaseType::Int}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Double, BaseType::Double}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Int, BaseType::Double}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Double, BaseType::Int}, BaseType::Bool},
|
|
|
|
|
{{BaseType::Char, BaseType::Char}, BaseType::Bool},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-02 08:59:18 +10:00
|
|
|
void TypeChecker::checkLiteralNodeType(Node& node) {
|
|
|
|
|
auto literal = node.getLiteral();
|
|
|
|
|
if (!literal.has_value()) {
|
|
|
|
|
throw std::runtime_error("FIXME literal node does not contain literal");
|
|
|
|
|
}
|
|
|
|
|
switch (literal->type) {
|
|
|
|
|
case LiteralType::None:
|
|
|
|
|
node.ptype = {{BaseType::None}};
|
|
|
|
|
break;
|
|
|
|
|
case LiteralType::String:
|
|
|
|
|
node.ptype = {{BaseType::String}};
|
|
|
|
|
break;
|
|
|
|
|
case LiteralType::Int:
|
|
|
|
|
node.ptype = {{BaseType::Int}};
|
|
|
|
|
break;
|
|
|
|
|
case LiteralType::Double:
|
|
|
|
|
node.ptype = {{BaseType::Double}};
|
|
|
|
|
break;
|
|
|
|
|
case LiteralType::Bool:
|
|
|
|
|
node.ptype = {{BaseType::Bool}};
|
|
|
|
|
break;
|
|
|
|
|
case LiteralType::Char:
|
|
|
|
|
node.ptype = {{BaseType::Char}};
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TypeChecker::checkIdentifierNodeType(Node& node) {
|
|
|
|
|
auto identifier = node.getIdentifier();
|
|
|
|
|
if (!identifier.has_value()) {
|
|
|
|
|
throw std::runtime_error("FIXME identifier node does not contain identifier");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (variables.find(*identifier) == variables.end()) {
|
|
|
|
|
throw std::runtime_error("unknown variable " + *identifier);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-02 14:09:38 +10:00
|
|
|
node.ptype = {{variables[*identifier]}};
|
2026-08-02 08:59:18 +10:00
|
|
|
}
|
|
|
|
|
|
2026-08-02 09:30:50 +10:00
|
|
|
void TypeChecker::checkTupleNodeType(Node& node) {
|
|
|
|
|
for (auto& child : node.children) {
|
|
|
|
|
checkNodeType(child);
|
|
|
|
|
}
|
2026-08-02 14:09:38 +10:00
|
|
|
// TODO implemnt tuple type checking
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TypeChecker::checkBindNodeType(Node& node) {
|
|
|
|
|
auto id = node.children[0].getIdentifier();
|
|
|
|
|
if (!id.has_value()) {
|
|
|
|
|
throw std::runtime_error("FIXME identifier node does not contain identifier");
|
|
|
|
|
}
|
|
|
|
|
if (variables.find(*id) != variables.end()) {
|
|
|
|
|
throw std::runtime_error("Cannot overwrite existing bind/variable/function with new bind");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
checkNodeType(node.children[1]);
|
|
|
|
|
auto nodeType = node.children[1].ptype.getOnlyType();
|
|
|
|
|
if (!nodeType.has_value()) {
|
|
|
|
|
throw std::runtime_error("cannot assign ambiguous type to name");
|
|
|
|
|
}
|
|
|
|
|
variables[*id] = *nodeType;
|
|
|
|
|
variables[*id].isConstant = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TypeChecker::checkFunctionBindNodeType(Node& node) {
|
|
|
|
|
auto id = node.children[0].getIdentifier();
|
|
|
|
|
if (!id.has_value()) {
|
|
|
|
|
throw std::runtime_error("FIXME identifier node does not contain identifier");
|
|
|
|
|
}
|
|
|
|
|
if (variables.find(*id) != variables.end()) {
|
|
|
|
|
throw std::runtime_error("Cannot overwrite existing bind/variable/function with new function");
|
|
|
|
|
}
|
2026-08-02 09:30:50 +10:00
|
|
|
|
2026-08-02 14:09:38 +10:00
|
|
|
checkNodeType(node.children[1]);
|
|
|
|
|
auto nodeType = node.children[1].ptype.getOnlyType();
|
|
|
|
|
if (!nodeType.has_value()) {
|
|
|
|
|
throw std::runtime_error("cannot assign ambiguous type to name");
|
|
|
|
|
}
|
|
|
|
|
variables[*id] = *nodeType;
|
|
|
|
|
variables[*id].isConstant = true;
|
|
|
|
|
}
|
2026-08-02 09:30:50 +10:00
|
|
|
|
2026-08-02 14:09:38 +10:00
|
|
|
void TypeChecker::checkSetNodeType(Node& node) {
|
|
|
|
|
auto id = node.children[0].getIdentifier();
|
|
|
|
|
if (!id.has_value()) {
|
|
|
|
|
throw std::runtime_error("FIXME identifier node does not contain identifier");
|
|
|
|
|
}
|
|
|
|
|
if (variables.find(*id) != variables.end()) {
|
|
|
|
|
if (variables[*id].isConstant) {
|
|
|
|
|
throw std::runtime_error("cannot reassign existing bind");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!inFunction) {
|
|
|
|
|
throw std::runtime_error("mutable variables may only be used in functions");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
checkNodeType(node.children[1]);
|
|
|
|
|
auto nodeType = node.children[1].ptype.getOnlyType();
|
|
|
|
|
if (!nodeType.has_value()) {
|
|
|
|
|
throw std::runtime_error("cannot assign ambiguous type to name");
|
|
|
|
|
}
|
|
|
|
|
variables[*id] = *nodeType;
|
2026-08-02 09:30:50 +10:00
|
|
|
}
|
2026-08-02 08:59:18 +10:00
|
|
|
|
2026-08-02 14:09:38 +10:00
|
|
|
void TypeChecker::checkCodeBlockType(Node& node) {}
|
|
|
|
|
|
|
|
|
|
|
2026-08-02 08:59:18 +10:00
|
|
|
void TypeChecker::checkNodeType(Node& node) {
|
|
|
|
|
switch (node.type) {
|
|
|
|
|
case NodeType::Root:
|
|
|
|
|
node.ptype = {{}}; // no possible types for root node
|
2026-08-02 14:09:38 +10:00
|
|
|
for (auto& child : node.children) {
|
|
|
|
|
checkNodeType(child);
|
|
|
|
|
}
|
2026-08-02 08:59:18 +10:00
|
|
|
break;
|
|
|
|
|
case NodeType::Literal:
|
|
|
|
|
checkLiteralNodeType(node);
|
|
|
|
|
break;
|
|
|
|
|
case NodeType::Expression:
|
|
|
|
|
checkNodeType(node.children[0]);
|
|
|
|
|
break;
|
|
|
|
|
case NodeType::Identifier:
|
|
|
|
|
checkIdentifierNodeType(node);
|
|
|
|
|
break;
|
|
|
|
|
case NodeType::Tuple:
|
|
|
|
|
checkTupleNodeType(node);
|
|
|
|
|
break;
|
2026-08-02 14:09:38 +10:00
|
|
|
case NodeType::Bind:
|
|
|
|
|
checkBindNodeType(node);
|
|
|
|
|
break;
|
|
|
|
|
case NodeType::FunctionBind:
|
|
|
|
|
checkFunctionBindNodeType(node);
|
|
|
|
|
break;
|
|
|
|
|
case NodeType::Set:
|
|
|
|
|
checkSetNodeType(node);
|
|
|
|
|
break;
|
|
|
|
|
case NodeType::CodeBlock:
|
|
|
|
|
checkCodeBlockType(node);
|
|
|
|
|
break;
|
|
|
|
|
|
2026-08-02 08:59:18 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TypeChecker::checkTypes() {
|
|
|
|
|
checkNodeType(input);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-01 22:12:05 +10:00
|
|
|
|
|
|
|
|
}
|