Add FunctionCall analysis

This commit is contained in:
augustin64 2024-01-03 17:25:43 +01:00
parent 70aebb35a6
commit 1bfbd45c4a
5 changed files with 85 additions and 11 deletions

View File

@ -277,17 +277,25 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
throw RuntimeError(ErrorType::NestedFunction, token.pos, {}); throw RuntimeError(ErrorType::NestedFunction, token.pos, {});
} catch (const InternalError& _) {} } catch (const InternalError& _) {}
FunctionPrototype prototype = make_fn_prototype(node);
Type type = {
.type=TypeType::Function,
.data=prototype
};
if (memory.contains(function_name)) { if (memory.contains(function_name)) {
// Il existe une variable non fonction à ce nom // Il existe une variable non fonction à ce nom
if (memory.get(function_name).type.type != TypeType::Function) { if (memory.get(function_name).type.type != TypeType::Function) {
throw RuntimeError(ErrorType::IncompatibleRedefinition, token.pos, function_name); throw RuntimeError(ErrorType::IncompatibleRedefinition, token.pos, function_name);
} }
Type old_type = memory.get(function_name).type;
if (!equal_types(type, old_type)) {
throw TypeError(ErrorType::IncompatibleDefinition, token.pos, function_name);
}
} }
memory.declare(function_name, { memory.declare(function_name, type, return_type_token.pos);
.type = TypeType::Function,
.data = make_fn_prototype(node)
}, return_type_token.pos);
return {}; return {};
} break; } break;
@ -310,6 +318,10 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
throw RuntimeError(ErrorType::FunctionRedefinition, token.pos, function_name); throw RuntimeError(ErrorType::FunctionRedefinition, token.pos, function_name);
FunctionPrototype prototype = make_fn_prototype(node); FunctionPrototype prototype = make_fn_prototype(node);
Type type = {
.type=TypeType::Function,
.data=prototype
};
if (memory.contains(function_name)) { if (memory.contains(function_name)) {
// Il existe une variable non fonction à ce nom // Il existe une variable non fonction à ce nom
@ -317,13 +329,13 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
throw RuntimeError(ErrorType::IncompatibleRedefinition, token.pos, function_name); throw RuntimeError(ErrorType::IncompatibleRedefinition, token.pos, function_name);
} }
//TODO: Vérification de la cohérence des arguments avec ceux du prototype Type old_type = memory.get(function_name).type;
if (!equal_types(type, old_type)) {
throw TypeError(ErrorType::IncompatibleDefinition, token.pos, function_name);
}
} }
memory.declare(function_name, { memory.declare(function_name, type, return_type_token.pos);
.type=TypeType::Function,
.data=prototype
}, return_type_token.pos);
memory.update(function_name, node.children[3]); memory.update(function_name, node.children[3]);
@ -339,6 +351,24 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
return {}; return {};
} break; } break;
case NodeType::FunctionCall: {
Token token = get<Token>(node.children[0]);
string function_name = get<string>(token.data);
if (!memory.contains(function_name))
throw RuntimeError(ErrorType::UnknownIdentifier, token.pos, function_name);
if (memory.contains(function_name) && !memory.get(function_name).initialized)
throw RuntimeError(ErrorType::UninitializedIdentifier, token.pos, function_name);
MemoryVar function = memory.get(function_name);
if (get<InnerNode>(node.children[1]).children.size() != get<FunctionPrototype>(function.type.data).size()-1)
throw RuntimeError(ErrorType::UnexpectedArgumentCount, token.pos, int(get<FunctionPrototype>(function.type.data).size())-1);
return get<0>(get<FunctionPrototype>(function.type.data).at(0));
} break;
default: default:
return {}; return {};
} }

View File

@ -27,6 +27,8 @@ string UserError::get_message(void) const {
case ErrorType::NestedFunction: return "Function definition is not allowed here"; case ErrorType::NestedFunction: return "Function definition is not allowed here";
case ErrorType::FunctionRedefinition: return "Redefinition of '"+get<string>(this->data)+"'"; case ErrorType::FunctionRedefinition: return "Redefinition of '"+get<string>(this->data)+"'";
case ErrorType::IncompatibleRedefinition: return "Redefinition of '"+get<string>(this->data)+"' as different kind of symbol"; case ErrorType::IncompatibleRedefinition: return "Redefinition of '"+get<string>(this->data)+"' as different kind of symbol";
case ErrorType::IncompatibleDefinition: return "Declaration of '"+get<string>(this->data)+"' is incompatible with previous prototype";
case ErrorType::UnexpectedArgumentCount: return "Expected "+to_string(get<int>(this->data))+" arguments to function call";
case ErrorType::ExpectedArithmeticType: return "Expression must have arithmetic type"; case ErrorType::ExpectedArithmeticType: return "Expression must have arithmetic type";
case ErrorType::UnknownIdentifier: return "Unknown identifier '"+get<string>(this->data)+"'"; case ErrorType::UnknownIdentifier: return "Unknown identifier '"+get<string>(this->data)+"'";
case ErrorType::AlreadyDeclaredIdentifier: return "Already declared identifier '"+get<string>(this->data)+"'"; case ErrorType::AlreadyDeclaredIdentifier: return "Already declared identifier '"+get<string>(this->data)+"'";

View File

@ -35,7 +35,9 @@ enum class ErrorType {
ExpectedArithmeticType, ExpectedArithmeticType,
NestedFunction, NestedFunction,
FunctionRedefinition, FunctionRedefinition,
IncompatibleRedefinition, IncompatibleRedefinition, // redefinition of a variable as a function
IncompatibleDefinition, // function declaration incompatible with prototype
UnexpectedArgumentCount,
// Runtime // Runtime
UnknownIdentifier, UnknownIdentifier,
@ -48,7 +50,7 @@ enum class ErrorType {
ControlReachesEndOfNonVoidFn ControlReachesEndOfNonVoidFn
}; };
using ErrorData = variant<monostate, string>; using ErrorData = variant<monostate, string, int>;
class UserError : public exception { class UserError : public exception {
public: public:

View File

@ -34,4 +34,9 @@ vector<string> split_string(const string& input, char delimiter);
*/ */
string _debug_get_type_type_name(TypeType type); string _debug_get_type_type_name(TypeType type);
/**
* Check if two types are equal
*/
bool equal_types(Type type1, Type type2);
#endif #endif

View File

@ -76,4 +76,39 @@ string _debug_get_type_type_name(TypeType type) {
case TypeType::Function: return "FUNCTION"; case TypeType::Function: return "FUNCTION";
default: return "Unknown"; default: return "Unknown";
} }
}
bool equal_types(Type type1, Type type2) {
if (type1.type != type2.type)
return false;
switch (type1.type) {
case TypeType::Int:
case TypeType::Double:
case TypeType::Void:
return true;
break;
case TypeType::Function: {
if (holds_alternative<monostate>(type1.data) != holds_alternative<monostate>(type2.data))
return false;
if (holds_alternative<monostate>(type1.data))
return true;
FunctionPrototype args1 = get<FunctionPrototype>(type1.data);
FunctionPrototype args2 = get<FunctionPrototype>(type2.data);
if (args1.size() != args2.size())
return false;
for (int i=0; i < (int)args1.size(); i++) {
if (!equal_types(get<0>(args1.at(i)), get<0>(args2.at(i))))
return false;
}
return true;
}
default:
return false;
break;
}
} }