c-repl/src/analysis.cpp

425 lines
17 KiB
C++

#include <iostream>
#include "include/utils.h"
#include "include/errors.h"
#include "include/analysis.h"
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 {
Type type = string_to_type(type_name);
return type;
} catch (...) {
throw TypeError(ErrorType::UnknownType, pos, type_name);
}
}
AnalysisResult analyze(Node &ast, Memory &memory) {
if (holds_alternative<Token>(ast)) {
Token token = get<Token>(ast);
switch (token.type) {
case TokenType::Litteral: {
if (holds_alternative<int>(token.data)) {
return type_type_to_type(TypeType::Int);
} else if (holds_alternative<double>(token.data)) {
return type_type_to_type(TypeType::Double);
}
throw exception();
break;
}
case TokenType::Identifier: {
string identifier = get<string>(token.data);
if (!memory.contains(identifier))
throw TypeError(ErrorType::UnknownIdentifier, token.pos, identifier);
return memory.get(identifier).type;
throw exception();
break;
}
case TokenType::Break:
case TokenType::Continue:
return {};
default:
throw exception();
}
} else {
InnerNode node = get<InnerNode>(ast);
switch (node.type) {
case NodeType::Prog:
analyze(node.children[0], memory);
analyze(node.children[1], memory);
return {};
break;
case NodeType::Epsilon:
return {};
break;
case NodeType::If:
case NodeType::IfElse: {
if (!bool_castable(analyze(node.children[0], memory))) {
throw TypeError(ErrorType::TypeNotCastable, get_node_pos(node.children[0]), "bool");
}
analyze(node.children[1], memory);
if (node.type == NodeType::IfElse)
analyze(node.children[2], memory);
return {};
} break;
case NodeType::While: {
if (!bool_castable(analyze(node.children[0], memory))) {
throw TypeError(ErrorType::TypeNotCastable, get_node_pos(node.children[0]), "bool");
}
analyze(node.children[1], memory);
return {};
} break;
case NodeType::For: {
memory.add_scope(ScopeType::For);
analyze(node.children[0], memory);
if (!bool_castable(analyze(node.children[1], memory))) {
throw TypeError(ErrorType::TypeNotCastable, get_node_pos(node.children[1]), "bool");
}
analyze(node.children[2], memory);
analyze(node.children[3], memory);
memory.remove_scope();
return {};
} break;
case NodeType::Bloc: {
memory.add_scope(ScopeType::Block);
analyze(node.children[0], memory);
memory.remove_scope();
return {};
} break;
case NodeType::Lor:
case NodeType::Land: {
if (!bool_castable(analyze(node.children[0], memory))) {
throw TypeError(ErrorType::TypeNotCastable, get_node_pos(node.children[0]), "bool");
}
if (!bool_castable(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))) {
throw TypeError(ErrorType::TypeNotCastable, get_node_pos(node.children[0]), "bool");
}
return type_type_to_type(TypeType::Int);
} break;
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);
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);
return get_cast(res1, res2, node.pos);
} break;
case NodeType::Mod: {
AnalysisResult e1 = analyze(node.children[0], memory);
AnalysisResult e2 = analyze(node.children[1], memory);
if (holds_alternative<monostate>(e1) || get<Type>(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) {
throw TypeError(ErrorType::ExpectedIntegralType, get_node_pos(node.children[1]));
}
return type_type_to_type(TypeType::Int);
} break;
case NodeType::UnaryPlus:
case NodeType::UnaryMinus: {
AnalysisResult res = analyze(node.children[0], memory);
if (holds_alternative<monostate>(res) || !is_arithmetic_type(get<Type>(res))) {
throw TypeError(ErrorType::ExpectedArithmeticType, get_node_pos(node.children[0]));
}
return get<Type>(res);
} break;
case NodeType::Declaration: {
Token type_token = get<Token>(node.children[0]);
string type_string = get<string>(type_token.data);
Token token = get<Token>(node.children[1]);
string identifier = get<string>(token.data);
if (memory.contains_top(identifier))
throw TypeError(ErrorType::AlreadyDeclaredIdentifier, token.pos, identifier);
Type type = try_string_to_type(type_string, type_token.pos);
if (type.type == TypeType::Void)
throw TypeError(ErrorType::IncompleteType, token.pos, {});
memory.declare(identifier, type);
return {};
} break;
case NodeType::AssignedDeclaration: {
Token type_token = get<Token>(node.children[0]);
string type_string = get<string>(type_token.data);
Token token = get<Token>(node.children[1]);
string identifier = get<string>(token.data);
if (memory.contains_top(identifier))
throw TypeError(ErrorType::AlreadyDeclaredIdentifier, token.pos, identifier);
Type type = try_string_to_type(type_string, type_token.pos);
if (type.type == TypeType::Void)
throw TypeError(ErrorType::IncompleteType, token.pos, {});
memory.declare(identifier, type);
get_cast(type, analyze(node.children[2], memory), get_node_pos(node));
return type;
} break;
case NodeType::Assignment: {
Token identifierTok = get<Token>(node.children[0]);
string identifier = get<string>(identifierTok.data);
if (!memory.contains(identifier))
throw TypeError(ErrorType::UnknownIdentifier, identifierTok.pos, identifier);
Type type = memory.get(identifier).type;
AnalysisResult res = analyze(node.children[1], memory);
get_cast(type, res, get_node_pos(node.children[1]));
return type;
} break;
case NodeType::LIncr:
case NodeType::RIncr:
case NodeType::LDecr:
case NodeType::RDecr: {
Token identifierTok = get<Token>(node.children[0]);
string identifier = get<string>(identifierTok.data);
if (!memory.contains(identifier))
throw TypeError(ErrorType::UnknownIdentifier, identifierTok.pos, identifier);
return memory.get(identifier).type;
}
case NodeType::Comma: {
analyze(node.children[0], memory);
return analyze(node.children[1], memory);
}
case NodeType::FunctionPrototype: {
Token return_type_token = get<Token>(node.children[0]);
string return_type_string = get<string>(return_type_token.data);
Type return_type = try_string_to_type(return_type_string, return_type_token.pos);
Token token = get<Token>(node.children[1]);
string function_name = get<string>(token.data);
try {
memory.get_function_scope();
throw TypeError(ErrorType::NestedFunction, token.pos, {});
} catch (const InternalError& _) {}
FunctionPrototype prototype = make_fn_prototype(node);
Type type = {
.type=TypeType::Function,
.data=prototype
};
if (memory.contains(function_name)) {
// Il existe une variable non fonction à ce nom
if (memory.get(function_name).type.type != TypeType::Function) {
throw TypeError(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);
}
}
for (int i=1; i < (int)prototype.size(); i++) {
if (get<0>(prototype.at(i)).type == TypeType::Void) {
InnerNode args_node = get<InnerNode>(node.children[2]);
InnerNode declaration_node = get<InnerNode>(args_node.children[i-1]);
CodePosition type_pos = get_node_pos(declaration_node.children[0]);
throw TypeError(ErrorType::IncompleteType, type_pos, {});
}
}
memory.declare(function_name, type);
return {};
} break;
case NodeType::FunctionDeclaration: {
Token return_type_token = get<Token>(node.children[0]);
string return_type_string = get<string>(return_type_token.data);
Type return_type = try_string_to_type(return_type_string, return_type_token.pos);
Token token = get<Token>(node.children[1]);
string function_name = get<string>(token.data);
// Déclarée dans une fonction
try {
memory.get_function_scope();
throw TypeError(ErrorType::NestedFunction, token.pos, {});
} catch (const InternalError& _) {}
// Corps de la fonction déjà déclaré
if (memory.contains(function_name) && memory.get(function_name).initialized)
throw TypeError(ErrorType::FunctionRedefinition, token.pos, function_name);
FunctionPrototype prototype = make_fn_prototype(node);
Type type = {
.type=TypeType::Function,
.data=prototype
};
if (memory.contains(function_name)) {
// Il existe une variable non fonction à ce nom
if (memory.get(function_name).type.type != TypeType::Function) {
throw TypeError(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);
}
}
for (int i=1; i < (int)prototype.size(); i++) {
if (get<0>(prototype.at(i)).type == TypeType::Void) {
InnerNode args_node = get<InnerNode>(node.children[2]);
InnerNode declaration_node = get<InnerNode>(args_node.children[i-1]);
CodePosition type_pos = get_node_pos(declaration_node.children[0]);
throw TypeError(ErrorType::IncompleteType, type_pos, {});
}
}
memory.declare(function_name, type);
Function fn = { {}, memory.make_closure() };
memory.update(function_name, fn);
memory.add_scope(ScopeType::Function, &memory.get(function_name));
for (tuple<Type, string> variable : prototype) {
Type vtype = get<0>(variable);
string vname = get<1>(variable);
memory.declare(vname, vtype);
}
analyze(node.children[3], memory);
memory.remove_scope();
return {};
} break;
case NodeType::FunctionCall: {
Token token = get<Token>(node.children[0]);
string function_name = get<string>(token.data);
if (!memory.contains(function_name))
throw TypeError(ErrorType::UnknownIdentifier, 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 TypeError(ErrorType::UnexpectedArgumentCount, token.pos, int(get<FunctionPrototype>(function.type.data).size())-1);
return get<0>(get<FunctionPrototype>(function.type.data).at(0));
} break;
case NodeType::Return: {
MemoryVar function;
try {
function = *memory.get_function_scope().fn;
} catch (const InternalError& _) {
throw ControlError(ErrorType::UnexpectedReturn, node.pos, {});
}
FunctionPrototype prototype = get<FunctionPrototype>(function.type.data);
Type return_type = get<0>(prototype.at(0));
if (return_type.type == TypeType::Void) {
if (node.children.size() != 0)
throw TypeError(ErrorType::IncompatibleReturnValue, node.pos, {});
return {};
}
if (node.children.size() == 0)
throw TypeError(ErrorType::IncompatibleReturnValue, node.pos, {});
AnalysisResult res = analyze(node.children[0], memory);
AnalysisResult expected_type = return_type;
get_cast(res, expected_type, node.pos);
return {};
}
default:
return {};
}
}
throw exception();
}