Type checking for functions

This commit is contained in:
2025-12-29 10:30:07 +11:00
parent c266728ff0
commit ca8db171d9

View File

@@ -21,7 +21,7 @@ namespace Solstice {
if (variables.find(i.outputId) != variables.end()) { if (variables.find(i.outputId) != variables.end()) {
return variables[i.outputId]; return variables[i.outputId];
} else { } else {
Error::syntaxError("Unknown variable", i.line, i.lineContent); Error::syntaxError("Unknown variable " + i.outputId, i.line, i.lineContent);
} }
break; break;
} }
@@ -55,6 +55,63 @@ namespace Solstice {
return "int"; return "int";
break; break;
} }
case SolNodeType::FunctionCall: {
if (i.children[0].outputId == "input") {
if (i.children.size() > 1) {
ensure(i.children[1], "string", "input function argument");
}
return "string";
}
std::string funcName = i.children[0].outputId;
if (variables.find(funcName) == variables.end()) {
Error::syntaxError("Unknown function " + funcName, i.line, i.lineContent);
}
std::string signature = variables[funcName];
// Parse signature: fun(arg1, arg2) retType
if (signature.rfind("fun(", 0) != 0) {
// Not a function signature, maybe it's a variable being called?
Error::typingError(funcName + " is not a function", i.line, i.lineContent);
}
size_t endParen = signature.find(')');
if (endParen == std::string::npos) {
Error::typingError("Invalid function signature for " + funcName, i.line, i.lineContent);
}
std::string argsStr = signature.substr(4, endParen - 4);
std::string retType = signature.substr(endParen + 2); // skip ") "
std::vector<std::string> expectedArgs;
if (!argsStr.empty()) {
size_t start = 0;
size_t end = argsStr.find(", ");
while (end != std::string::npos) {
expectedArgs.push_back(argsStr.substr(start, end - start));
start = end + 2;
end = argsStr.find(", ", start);
}
expectedArgs.push_back(argsStr.substr(start));
}
// Check arguments
// children[0] is function name, children[1..] are args
if (i.children.size() - 1 != expectedArgs.size()) {
Error::typingError("Expected " + std::to_string(expectedArgs.size()) + " arguments for " + funcName + ", got " + std::to_string(i.children.size() - 1), i.line, i.lineContent);
return "none";
}
for (size_t k = 0; k < expectedArgs.size(); ++k) {
std::string argType = checkNodeReturnType(i.children[k + 1]);
if (argType != expectedArgs[k]) {
Error::typingError("Expected argument " + std::to_string(k + 1) + " of " + funcName + " to be " + expectedArgs[k] + ", got " + argType, i.children[k + 1].line, i.children[k + 1].lineContent);
}
}
return retType;
break;
}
case SolNodeType::Puts: case SolNodeType::Puts:
case SolNodeType::If: case SolNodeType::If:
case SolNodeType::While: case SolNodeType::While:
@@ -602,6 +659,7 @@ namespace Solstice {
break; break;
} }
case SolNodeType::FunctionCall: { case SolNodeType::FunctionCall: {
checkNodeReturnType(*this);
// Take care of in built functions // Take care of in built functions
// This will be removed when inline ground is added // This will be removed when inline ground is added
if (children[0].outputId == "input") { if (children[0].outputId == "input") {
@@ -635,6 +693,10 @@ namespace Solstice {
break; break;
} }
case SolNodeType::Return: { case SolNodeType::Return: {
std::string retType = checkNodeReturnType(children[0]);
if (currentFunctionRetType != "" && retType != currentFunctionRetType) {
Error::typingError("Expected return type " + currentFunctionRetType + " but got " + retType, children[0].line, children[0].lineContent);
}
SolGroundCodeBlock codeBlock; SolGroundCodeBlock codeBlock;
GroundInstruction returnInstruction = groundCreateInstruction(RETURN); GroundInstruction returnInstruction = groundCreateInstruction(RETURN);
groundAddReferenceToInstruction(&returnInstruction, groundCreateReference(VALREF, children[0].outputId.data())); groundAddReferenceToInstruction(&returnInstruction, groundCreateReference(VALREF, children[0].outputId.data()));
@@ -654,21 +716,34 @@ namespace Solstice {
strcpy(retType, function.returnType.c_str()); strcpy(retType, function.returnType.c_str());
groundAddReferenceToInstruction(&inst, groundCreateReference(FNREF, fnName)); groundAddReferenceToInstruction(&inst, groundCreateReference(FNREF, fnName));
groundAddReferenceToInstruction(&inst, groundCreateReference(TYPEREF, retType)); groundAddReferenceToInstruction(&inst, groundCreateReference(TYPEREF, retType));
// Scope Management
auto variablebackup = variables; auto variablebackup = variables;
variables.clear(); std::string oldRetType = currentFunctionRetType;
variables.clear();
currentFunctionRetType = function.returnType;
for (auto& pair : function.parameters) { for (auto& pair : function.parameters) {
groundAddReferenceToInstruction(&inst, groundCreateReference(TYPEREF, pair.second.data())); groundAddReferenceToInstruction(&inst, groundCreateReference(TYPEREF, pair.second.data()));
groundAddReferenceToInstruction(&inst, groundCreateReference(DIRREF, pair.first.data())); groundAddReferenceToInstruction(&inst, groundCreateReference(DIRREF, pair.first.data()));
variables[pair.second] = pair.first; variables[pair.first] = pair.second; // Correctly map Name -> Type
} }
variables = variablebackup;
codeBlock.code.push_back(inst); codeBlock.code.push_back(inst);
code.push_back(codeBlock); code.push_back(codeBlock);
codeBlock.code.clear(); codeBlock.code.clear();
// Generate body code with local variables visible
auto childCode = function.content->generateCode(); auto childCode = function.content->generateCode();
code.insert(code.end(), childCode.begin(), childCode.end()); code.insert(code.end(), childCode.begin(), childCode.end());
codeBlock.code.push_back(groundCreateInstruction(ENDFUN)); codeBlock.code.push_back(groundCreateInstruction(ENDFUN));
code.push_back(codeBlock); code.push_back(codeBlock);
// Restore Scope
variables = variablebackup;
currentFunctionRetType = oldRetType;
} }
break; break;
} }