#include #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(res1) || holds_alternative(res2)) { throw TypeError(ErrorType::TypesNotComparable, pos); } Type type1 = get(res1); Type type2 = get(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(ast)) { Token token = get(ast); switch (token.type) { case TokenType::Litteral: { if (holds_alternative(token.data)) { return type_type_to_type(TypeType::Int); } else if (holds_alternative(token.data)) { return type_type_to_type(TypeType::Double); } throw exception(); break; } case TokenType::Identifier: { string identifier = get(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(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(e1) || get(e1).type != TypeType::Int) { throw TypeError(ErrorType::ExpectedIntegralType, get_node_pos(node.children[0])); } if (holds_alternative(e2) || get(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(res) || !is_arithmetic_type(get(res))) { throw TypeError(ErrorType::ExpectedArithmeticType, get_node_pos(node.children[0])); } return get(res); } break; case NodeType::Declaration: { Token type_token = get(node.children[0]); string type_string = get(type_token.data); Token token = get(node.children[1]); string identifier = get(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(node.children[0]); string type_string = get(type_token.data); Token token = get(node.children[1]); string identifier = get(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(node.children[0]); string identifier = get(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(node.children[0]); string identifier = get(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(node.children[0]); string return_type_string = get(return_type_token.data); Type return_type = try_string_to_type(return_type_string, return_type_token.pos); Token token = get(node.children[1]); string function_name = get(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(node.children[2]); InnerNode declaration_node = get(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(node.children[0]); string return_type_string = get(return_type_token.data); Type return_type = try_string_to_type(return_type_string, return_type_token.pos); Token token = get(node.children[1]); string function_name = get(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(node.children[2]); InnerNode declaration_node = get(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 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(node.children[0]); string function_name = get(token.data); if (!memory.contains(function_name)) throw TypeError(ErrorType::UnknownIdentifier, token.pos, function_name); MemoryVar function = memory.get(function_name); if (get(node.children[1]).children.size() != get(function.type.data).size()-1) throw TypeError(ErrorType::UnexpectedArgumentCount, token.pos, int(get(function.type.data).size())-1); return get<0>(get(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(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(); }