Add casting.cpp

This commit is contained in:
augustin64 2024-01-12 15:40:03 +01:00
parent 553c820a8a
commit cc218d0834
12 changed files with 259 additions and 130 deletions

View File

@ -1,53 +1,8 @@
#include <iostream> #include <iostream>
#include "include/utils.hpp"
#include "include/errors.hpp" #include "include/errors.hpp"
#include "include/analysis.hpp" #include "include/analysis.hpp"
#include "include/casting.hpp"
bool bool_castable(AnalysisResult type) {
(void)type;
return true;
}
void check_comparable(AnalysisResult res1, AnalysisResult res2, CodePosition pos) {
if (holds_alternative<monostate>(res1) || holds_alternative<monostate>(res2)) {
throw TypeError(ErrorType::TypesNotComparable, pos);
}
Type type1 = get<Type>(res1);
Type type2 = get<Type>(res2);
switch (type1.type) {
case TypeType::Int:
case TypeType::Double: {
switch (type2.type) {
case TypeType::Int:
case TypeType::Double: {
return;
}
default:
throw TypeError(ErrorType::TypesNotComparable, pos);
}
}
default:
throw TypeError(ErrorType::TypesNotComparable, pos);
}
}
Type get_cast(AnalysisResult type1, AnalysisResult type2, CodePosition pos) {
(void)type1; (void)type2; (void)pos;
return type_type_to_type(TypeType::Int);
}
bool is_arithmetic_type(Type type) {
switch (type.type) {
case TypeType::Int:
case TypeType::Double:
return true;
default:
return false;
}
}
Type try_string_to_type(string type_name, CodePosition pos) { Type try_string_to_type(string type_name, CodePosition pos) {
try { try {
@ -58,7 +13,7 @@ Type try_string_to_type(string type_name, CodePosition pos) {
} }
} }
AnalysisResult analyze(Node &ast, Memory &memory) { Type analyze(Node &ast, Memory &memory) {
if (holds_alternative<Token>(ast)) { if (holds_alternative<Token>(ast)) {
Token token = get<Token>(ast); Token token = get<Token>(ast);
switch (token.type) { switch (token.type) {
@ -100,7 +55,7 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
break; break;
case NodeType::If: case NodeType::If:
case NodeType::IfElse: { case NodeType::IfElse: {
if (!bool_castable(analyze(node.children[0], memory))) { if (!is_arithmetic_type(analyze(node.children[0], memory))) {
throw TypeError(ErrorType::TypeNotCastable, get_node_pos(node.children[0]), "bool"); throw TypeError(ErrorType::TypeNotCastable, get_node_pos(node.children[0]), "bool");
} }
@ -111,7 +66,7 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
return {}; return {};
} break; } break;
case NodeType::While: { case NodeType::While: {
if (!bool_castable(analyze(node.children[0], memory))) { if (!is_arithmetic_type(analyze(node.children[0], memory))) {
throw TypeError(ErrorType::TypeNotCastable, get_node_pos(node.children[0]), "bool"); throw TypeError(ErrorType::TypeNotCastable, get_node_pos(node.children[0]), "bool");
} }
analyze(node.children[1], memory); analyze(node.children[1], memory);
@ -123,7 +78,7 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
analyze(node.children[0], memory); analyze(node.children[0], memory);
if (!bool_castable(analyze(node.children[1], memory))) { if (!is_arithmetic_type(analyze(node.children[1], memory))) {
throw TypeError(ErrorType::TypeNotCastable, get_node_pos(node.children[1]), "bool"); throw TypeError(ErrorType::TypeNotCastable, get_node_pos(node.children[1]), "bool");
} }
@ -142,17 +97,17 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
} break; } break;
case NodeType::Lor: case NodeType::Lor:
case NodeType::Land: { case NodeType::Land: {
if (!bool_castable(analyze(node.children[0], memory))) { if (!is_arithmetic_type(analyze(node.children[0], memory))) {
throw TypeError(ErrorType::TypeNotCastable, get_node_pos(node.children[0]), "bool"); throw TypeError(ErrorType::TypeNotCastable, get_node_pos(node.children[0]), "bool");
} }
if (!bool_castable(analyze(node.children[1], memory))) { if (!is_arithmetic_type(analyze(node.children[1], memory))) {
throw TypeError(ErrorType::TypeNotCastable, get_node_pos(node.children[1]), "bool"); throw TypeError(ErrorType::TypeNotCastable, get_node_pos(node.children[1]), "bool");
} }
return type_type_to_type(TypeType::Int); return type_type_to_type(TypeType::Int);
} break; } break;
case NodeType::Neg: { case NodeType::Neg: {
if (!bool_castable(analyze(node.children[0], memory))) { if (!is_arithmetic_type(analyze(node.children[0], memory))) {
throw TypeError(ErrorType::TypeNotCastable, get_node_pos(node.children[0]), "bool"); throw TypeError(ErrorType::TypeNotCastable, get_node_pos(node.children[0]), "bool");
} }
@ -161,33 +116,33 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
case NodeType::Lt: case NodeType::Lt:
case NodeType::Gt: case NodeType::Gt:
case NodeType::Leq: case NodeType::Leq:
case NodeType::Geq: { case NodeType::Geq:
AnalysisResult res1 = analyze(node.children[0], memory); case NodeType::Eq:
AnalysisResult res2 = analyze(node.children[1], memory); case NodeType::Neq: {
Type res1 = analyze(node.children[0], memory);
Type res2 = analyze(node.children[1], memory);
check_comparable(res1, res2, node.pos); check_comparable(res1, res2, node.pos);
return type_type_to_type(TypeType::Int); return type_type_to_type(TypeType::Int);
} break; } break;
case NodeType::Eq:
case NodeType::Neq:
case NodeType::Plus: case NodeType::Plus:
case NodeType::Minus: case NodeType::Minus:
case NodeType::Mult: case NodeType::Mult:
case NodeType::Div: { case NodeType::Div: {
AnalysisResult res1 = analyze(node.children[0], memory); Type res1 = analyze(node.children[0], memory);
AnalysisResult res2 = analyze(node.children[1], memory); Type res2 = analyze(node.children[1], memory);
return get_cast(res1, res2, node.pos); return get_operation_cast(res1, res2, node.pos);
} break; } break;
case NodeType::Mod: { case NodeType::Mod: {
AnalysisResult e1 = analyze(node.children[0], memory); Type e1 = analyze(node.children[0], memory);
AnalysisResult e2 = analyze(node.children[1], memory); Type e2 = analyze(node.children[1], memory);
if (holds_alternative<monostate>(e1) || get<Type>(e1).type != TypeType::Int) { if (e1.type != TypeType::Int) {
throw TypeError(ErrorType::ExpectedIntegralType, get_node_pos(node.children[0])); throw TypeError(ErrorType::ExpectedIntegralType, get_node_pos(node.children[0]));
} }
if (holds_alternative<monostate>(e2) || get<Type>(e2).type != TypeType::Int) { if (e2.type != TypeType::Int) {
throw TypeError(ErrorType::ExpectedIntegralType, get_node_pos(node.children[1])); throw TypeError(ErrorType::ExpectedIntegralType, get_node_pos(node.children[1]));
} }
@ -195,13 +150,13 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
} break; } break;
case NodeType::UnaryPlus: case NodeType::UnaryPlus:
case NodeType::UnaryMinus: { case NodeType::UnaryMinus: {
AnalysisResult res = analyze(node.children[0], memory); Type res = analyze(node.children[0], memory);
if (holds_alternative<monostate>(res) || !is_arithmetic_type(get<Type>(res))) { if (!is_arithmetic_type(res)) {
throw TypeError(ErrorType::ExpectedArithmeticType, get_node_pos(node.children[0])); throw TypeError(ErrorType::ExpectedArithmeticType, get_node_pos(node.children[0]));
} }
return get<Type>(res); return res;
} break; } break;
case NodeType::Declaration: { case NodeType::Declaration: {
Token type_token = get<Token>(node.children[0]); Token type_token = get<Token>(node.children[0]);
@ -237,7 +192,7 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
memory.declare(identifier, type); memory.declare(identifier, type);
get_cast(type, analyze(node.children[2], memory), get_node_pos(node)); check_cast(type, analyze(node.children[2], memory), get_node_pos(node));
return type; return type;
} break; } break;
@ -248,9 +203,9 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
throw TypeError(ErrorType::UnknownIdentifier, identifier_token.pos, identifier); throw TypeError(ErrorType::UnknownIdentifier, identifier_token.pos, identifier);
Type type = memory.get(identifier).type; Type type = memory.get(identifier).type;
AnalysisResult res = analyze(node.children[1], memory); Type res = analyze(node.children[1], memory);
get_cast(type, res, get_node_pos(node.children[1])); check_cast(type, res, get_node_pos(node.children[1]));
return type; return type;
} break; } break;
@ -408,10 +363,10 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
if (node.children.size() == 0) if (node.children.size() == 0)
throw TypeError(ErrorType::IncompatibleReturnValue, node.pos, {}); throw TypeError(ErrorType::IncompatibleReturnValue, node.pos, {});
AnalysisResult res = analyze(node.children[0], memory); Type res = analyze(node.children[0], memory);
AnalysisResult expected_type = return_type; Type expected_type = return_type;
get_cast(res, expected_type, node.pos); check_cast(res, expected_type, node.pos);
return {}; return {};
} }

101
src/casting.cpp Normal file
View File

@ -0,0 +1,101 @@
#include "include/casting.hpp"
#include "include/errors.hpp"
bool check_valid_cast_int(Type type) {
switch (type.type) {
case TypeType::Int:
case TypeType::Double:
return true;
default:
return false;
}
}
bool check_valid_cast_double(Type type) {
switch (type.type) {
case TypeType::Int:
case TypeType::Double:
return true;
default:
return false;
}
}
bool check_valid_cast(Type dest, Type type) {
switch (dest.type) {
case TypeType::Void: return true;
case TypeType::Function: return false;
case TypeType::Int: return check_valid_cast_int(type);
case TypeType::Double: return check_valid_cast_double(type);
default:
return false;
}
}
void check_cast(Type dest, Type type, CodePosition pos) {
if (!check_valid_cast(dest, type))
throw TypeError(ErrorType::TypeNotCastable, pos, type_type_to_string(type.type));
}
Type get_operation_cast(Type type1, Type type2, CodePosition pos) {
if (!is_arithmetic_type(type1) || !is_arithmetic_type(type2))
throw TypeError(ErrorType::ExpectedArithmeticType, pos, {});
if (type1.type == TypeType::Double || type2.type == TypeType::Double)
return type_type_to_type(TypeType::Double);
return type_type_to_type(TypeType::Int);
}
bool is_arithmetic_type(Type type) {
switch (type.type) {
case TypeType::Int:
case TypeType::Double:
return true;
default:
return false;
}
}
void check_comparable(Type type1, Type type2, CodePosition pos) {
if (!is_arithmetic_type(type1) || !is_arithmetic_type(type2))
throw TypeError(ErrorType::TypesNotComparable, pos);
}
bool bool_cast(EvalResult value) {
if (holds_alternative<int>(value)) {
return get<int>(value) != 0;
}
else if (holds_alternative<double>(value)) {
return get<double>(value) != 0;
}
else {
throw exception();
}
}
int int_cast(EvalResult value) {
if (holds_alternative<int>(value)) {
return get<int>(value);
} else if (holds_alternative<double>(value)) {
return int(get<double>(value));
}
else {
throw exception();
}
}
double double_cast(EvalResult value) {
if (holds_alternative<int>(value)) {
return double(get<int>(value));
}
else if (holds_alternative<double>(value)) {
return get<double>(value);
}
else {
throw exception();
}
}

View File

@ -1,12 +1,10 @@
#ifndef ANALYSIS_H #ifndef DEF_ANALYSIS_H
#define ANALYSIS_H #define DEF_ANALYSIS_H
#include "types.hpp" #include "types.hpp"
#include "memory.hpp" #include "memory.hpp"
using namespace std; using namespace std;
using AnalysisResult = variant<Type, monostate>; Type analyze(Node &ast, Memory &memory);
AnalysisResult analyze(Node &ast, Memory &memory);
#endif #endif

37
src/include/casting.hpp Normal file
View File

@ -0,0 +1,37 @@
#ifndef DEF_CASTING_H
#define DEF_CASTING_H
#include "utils.hpp"
#include "types.hpp"
/**
* Check if two types are comparable
*/
void check_comparable(Type type1, Type type2, CodePosition pos);
/**
* Check if cast possible de type vers dest
* Else throw error
*/
void check_cast(Type dest, Type type, CodePosition pos);
/**
* cast type de retour d'une opération entre type1 et type2
* Par exemple: (Int) / (Float) -> (Float)
* If impossible, throw error
*/
Type get_operation_cast(Type dest, Type type, CodePosition pos);
/**
* Check if type is arithmetic
* double, int, ...
*/
bool is_arithmetic_type(Type type);
bool bool_cast(EvalResult value);
int int_cast(EvalResult value);
double double_cast(EvalResult value);
#endif

View File

@ -1,5 +1,5 @@
#ifndef MEMORY_H #ifndef DEF_MEMORY_H
#define MEMORY_H #define DEF_MEMORY_H
#include <string> #include <string>
#include <iostream> #include <iostream>

View File

@ -1,5 +1,5 @@
#ifndef TOKENIZE_H #ifndef DEF_TOKENIZE_H
#define TOKENIZE_H #define DEF_TOKENIZE_H
#include <vector> #include <vector>
#include <string> #include <string>

View File

@ -1,5 +1,5 @@
#ifndef TYPES_H #ifndef DEF_TYPES_H
#define TYPES_H #define DEF_TYPES_H
#include <variant> #include <variant>
#include <string> #include <string>
@ -25,7 +25,7 @@ using FunctionPrototype = vector<ArgDefinition>;
using TypeData = variant<monostate, FunctionPrototype>; using TypeData = variant<monostate, FunctionPrototype>;
struct Type { struct Type {
TypeType type; TypeType type=TypeType::Void;
TypeData data { }; TypeData data { };
}; };

View File

@ -32,7 +32,7 @@ vector<string> split_string(const string& input, char delimiter);
/** /**
* Returns a human-readable name for a TypeType object * Returns a human-readable name for a TypeType object
*/ */
string _debug_get_type_type_name(TypeType type); string type_type_to_string(TypeType type);
/** /**
* Check if two types are equal * Check if two types are equal

View File

@ -1,46 +1,13 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <iostream> #include <iostream>
#include "include/parser.hpp"
#include "include/interpreter.hpp"
#include "include/memory.hpp"
#include "include/utils.hpp" #include "include/utils.hpp"
#include "include/memory.hpp"
#include "include/parser.hpp"
#include "include/casting.hpp"
#include "include/interpreter.hpp"
using namespace std; using namespace std;
bool bool_cast(EvalResult value) {
if (holds_alternative<int>(value)) {
return get<int>(value) != 0;
}
else if (holds_alternative<double>(value)) {
return get<double>(value) != 0;
}
else {
throw exception();
}
}
int int_cast(EvalResult value) {
if (holds_alternative<int>(value)) {
return get<int>(value);
} else if (holds_alternative<double>(value)) {
return int(get<double>(value));
}
else {
throw exception();
}
}
double double_cast(EvalResult value) {
if (holds_alternative<int>(value)) {
return double(get<int>(value));
}
else if (holds_alternative<double>(value)) {
return get<double>(value);
}
else {
throw exception();
}
}
EvalResult cast_from_type(Type type, EvalResult val) { EvalResult cast_from_type(Type type, EvalResult val) {
switch (type.type) { switch (type.type) {

View File

@ -187,7 +187,7 @@ void _debug_print_scope(Scope scope) {
cout << setw (14) << it.first << " "; cout << setw (14) << it.first << " ";
} }
cout << "|"; cout << "|";
cout << setw (9) << _debug_get_type_type_name(it.second.type.type) << " |"; cout << setw (9) << type_type_to_string(it.second.type.type) << " |";
_debug_print_memory_var(it.second); _debug_print_memory_var(it.second);
cout << endl; cout << endl;

View File

@ -68,12 +68,12 @@ vector<string> split_string(const string& input, char delimiter) {
return tokens; return tokens;
} }
string _debug_get_type_type_name(TypeType type) { string type_type_to_string(TypeType type) {
switch (type) { switch (type) {
case TypeType::Int: return "INT"; case TypeType::Int: return "int";
case TypeType::Double: return "DOUBLE"; case TypeType::Double: return "double";
case TypeType::Void: return "VOID"; case TypeType::Void: return "void";
case TypeType::Function: return "FUNCTION"; case TypeType::Function: return "function";
default: return "Unknown"; default: return "Unknown";
} }
} }

71
test/analysis.cpp Normal file
View File

@ -0,0 +1,71 @@
#include "include/test.hpp"
#include "../src/include/execute.hpp"
#include "../src/include/utils.hpp"
int execute(string s) {
Memory memory;
return get<int>(execute(split_string(s, '\n'), memory));
}
int main() {
_TEST_PRESENTATION("Analyse statique");
_TEST_ASSERT(
_TEST_IS_EXCEPTION(execute("unknown x;"), ErrorType::UnknownType),
"Type inconnu",
true
);
_TEST_ASSERT(
_TEST_IS_EXCEPTION(execute(R"(
void f() {}
int x = f();
)"), ErrorType::TypeNotCastable),
"Cast depuis void",
true
);
_TEST_ASSERT(
_TEST_IS_EXCEPTION(execute(R"(
void f() {}
5.0 / f();
)"), ErrorType::ExpectedArithmeticType),
"Division par void",
true
);
_TEST_ASSERT(
_TEST_IS_EXCEPTION(execute("5 % (5.0 / 2.0);"), ErrorType::ExpectedIntegralType),
"Modulo flottant",
true
);
_TEST_ASSERT(
_TEST_IS_EXCEPTION(execute("void x;"), ErrorType::IncompleteType),
"Variable de type void",
true
);
_TEST_ASSERT(
_TEST_IS_EXCEPTION(execute(R"(
int x;
int x;
)"), ErrorType::AlreadyDeclaredIdentifier),
"Redéclaration",
true
);
_TEST_ASSERT(
_TEST_IS_EXCEPTION(execute(R"(
int x;
void x() {}
)"), ErrorType::IncompatibleRedefinition),
"Redéfinition",
true
);
return 0;
}