Add casting.cpp
This commit is contained in:
parent
553c820a8a
commit
cc218d0834
103
src/analysis.cpp
103
src/analysis.cpp
@ -1,53 +1,8 @@
|
||||
#include <iostream>
|
||||
#include "include/utils.hpp"
|
||||
#include "include/errors.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) {
|
||||
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)) {
|
||||
Token token = get<Token>(ast);
|
||||
switch (token.type) {
|
||||
@ -100,7 +55,7 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
|
||||
break;
|
||||
case NodeType::If:
|
||||
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");
|
||||
}
|
||||
|
||||
@ -111,7 +66,7 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
|
||||
return {};
|
||||
} break;
|
||||
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");
|
||||
}
|
||||
analyze(node.children[1], memory);
|
||||
@ -123,7 +78,7 @@ AnalysisResult analyze(Node &ast, Memory &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");
|
||||
}
|
||||
|
||||
@ -142,17 +97,17 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
|
||||
} break;
|
||||
case NodeType::Lor:
|
||||
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");
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
return type_type_to_type(TypeType::Int);
|
||||
} break;
|
||||
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");
|
||||
}
|
||||
|
||||
@ -161,33 +116,33 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
|
||||
case NodeType::Lt:
|
||||
case NodeType::Gt:
|
||||
case NodeType::Leq:
|
||||
case NodeType::Geq: {
|
||||
AnalysisResult res1 = analyze(node.children[0], memory);
|
||||
AnalysisResult res2 = analyze(node.children[1], memory);
|
||||
case NodeType::Geq:
|
||||
case NodeType::Eq:
|
||||
case NodeType::Neq: {
|
||||
Type res1 = analyze(node.children[0], memory);
|
||||
Type res2 = analyze(node.children[1], memory);
|
||||
|
||||
check_comparable(res1, res2, node.pos);
|
||||
|
||||
return type_type_to_type(TypeType::Int);
|
||||
} break;
|
||||
case NodeType::Eq:
|
||||
case NodeType::Neq:
|
||||
case NodeType::Plus:
|
||||
case NodeType::Minus:
|
||||
case NodeType::Mult:
|
||||
case NodeType::Div: {
|
||||
AnalysisResult res1 = analyze(node.children[0], memory);
|
||||
AnalysisResult res2 = analyze(node.children[1], memory);
|
||||
Type res1 = analyze(node.children[0], memory);
|
||||
Type res2 = analyze(node.children[1], memory);
|
||||
|
||||
return get_cast(res1, res2, node.pos);
|
||||
return get_operation_cast(res1, res2, node.pos);
|
||||
} break;
|
||||
case NodeType::Mod: {
|
||||
AnalysisResult e1 = analyze(node.children[0], memory);
|
||||
AnalysisResult e2 = analyze(node.children[1], memory);
|
||||
Type e1 = analyze(node.children[0], 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]));
|
||||
}
|
||||
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]));
|
||||
}
|
||||
|
||||
@ -195,13 +150,13 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
|
||||
} break;
|
||||
case NodeType::UnaryPlus:
|
||||
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]));
|
||||
}
|
||||
|
||||
return get<Type>(res);
|
||||
return res;
|
||||
} break;
|
||||
case NodeType::Declaration: {
|
||||
Token type_token = get<Token>(node.children[0]);
|
||||
@ -237,7 +192,7 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
|
||||
|
||||
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;
|
||||
} break;
|
||||
@ -248,9 +203,9 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
|
||||
throw TypeError(ErrorType::UnknownIdentifier, identifier_token.pos, identifier);
|
||||
|
||||
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;
|
||||
} break;
|
||||
@ -408,10 +363,10 @@ AnalysisResult analyze(Node &ast, Memory &memory) {
|
||||
if (node.children.size() == 0)
|
||||
throw TypeError(ErrorType::IncompatibleReturnValue, node.pos, {});
|
||||
|
||||
AnalysisResult res = analyze(node.children[0], memory);
|
||||
AnalysisResult expected_type = return_type;
|
||||
Type res = analyze(node.children[0], memory);
|
||||
Type expected_type = return_type;
|
||||
|
||||
get_cast(res, expected_type, node.pos);
|
||||
check_cast(res, expected_type, node.pos);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
101
src/casting.cpp
Normal file
101
src/casting.cpp
Normal 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();
|
||||
}
|
||||
}
|
@ -1,12 +1,10 @@
|
||||
#ifndef ANALYSIS_H
|
||||
#define ANALYSIS_H
|
||||
#ifndef DEF_ANALYSIS_H
|
||||
#define DEF_ANALYSIS_H
|
||||
|
||||
#include "types.hpp"
|
||||
#include "memory.hpp"
|
||||
using namespace std;
|
||||
|
||||
using AnalysisResult = variant<Type, monostate>;
|
||||
|
||||
AnalysisResult analyze(Node &ast, Memory &memory);
|
||||
Type analyze(Node &ast, Memory &memory);
|
||||
|
||||
#endif
|
37
src/include/casting.hpp
Normal file
37
src/include/casting.hpp
Normal 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
|
@ -1,5 +1,5 @@
|
||||
#ifndef MEMORY_H
|
||||
#define MEMORY_H
|
||||
#ifndef DEF_MEMORY_H
|
||||
#define DEF_MEMORY_H
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
@ -1,5 +1,5 @@
|
||||
#ifndef TOKENIZE_H
|
||||
#define TOKENIZE_H
|
||||
#ifndef DEF_TOKENIZE_H
|
||||
#define DEF_TOKENIZE_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
@ -1,5 +1,5 @@
|
||||
#ifndef TYPES_H
|
||||
#define TYPES_H
|
||||
#ifndef DEF_TYPES_H
|
||||
#define DEF_TYPES_H
|
||||
|
||||
#include <variant>
|
||||
#include <string>
|
||||
@ -25,7 +25,7 @@ using FunctionPrototype = vector<ArgDefinition>;
|
||||
using TypeData = variant<monostate, FunctionPrototype>;
|
||||
|
||||
struct Type {
|
||||
TypeType type;
|
||||
TypeType type=TypeType::Void;
|
||||
TypeData data { };
|
||||
};
|
||||
|
||||
|
@ -32,7 +32,7 @@ vector<string> split_string(const string& input, char delimiter);
|
||||
/**
|
||||
* 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
|
||||
|
@ -1,46 +1,13 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "include/parser.hpp"
|
||||
#include "include/interpreter.hpp"
|
||||
#include "include/memory.hpp"
|
||||
#include "include/utils.hpp"
|
||||
#include "include/memory.hpp"
|
||||
#include "include/parser.hpp"
|
||||
#include "include/casting.hpp"
|
||||
#include "include/interpreter.hpp"
|
||||
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) {
|
||||
switch (type.type) {
|
||||
|
@ -187,7 +187,7 @@ void _debug_print_scope(Scope scope) {
|
||||
cout << setw (14) << it.first << " ";
|
||||
}
|
||||
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);
|
||||
cout << endl;
|
||||
|
@ -68,12 +68,12 @@ vector<string> split_string(const string& input, char delimiter) {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
string _debug_get_type_type_name(TypeType type) {
|
||||
string type_type_to_string(TypeType type) {
|
||||
switch (type) {
|
||||
case TypeType::Int: return "INT";
|
||||
case TypeType::Double: return "DOUBLE";
|
||||
case TypeType::Void: return "VOID";
|
||||
case TypeType::Function: return "FUNCTION";
|
||||
case TypeType::Int: return "int";
|
||||
case TypeType::Double: return "double";
|
||||
case TypeType::Void: return "void";
|
||||
case TypeType::Function: return "function";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
71
test/analysis.cpp
Normal file
71
test/analysis.cpp
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user