ArkScript
A small, fast, functional and scripting language for video games
TypeChecker.cpp
Go to the documentation of this file.
1#include <Ark/TypeChecker.hpp>
2
3#include <limits>
4#include <algorithm>
5#include <termcolor/proxy.hpp>
6
7#include <Ark/Exceptions.hpp>
8
9namespace Ark::types
10{
11 std::string typeListToString(const std::vector<ValueType>& types)
12 {
13 if (types.size() == 1 && types[0] == ValueType::Any)
14 return "any";
15
16 std::string acc;
17
18 for (std::size_t i = 0, end = types.size(); i < end; ++i)
19 {
20 if (i > 0)
21 acc += ", ";
22 acc += types_to_str[static_cast<std::size_t>(types[i])];
23 }
24 return acc;
25 }
26
27 void displayContract(const Contract& contract, const std::vector<Value>& args)
28 {
29 auto displayArg = [](const Typedef& td, bool correct) {
30 const std::string arg_str = typeListToString(td.types);
31
32 std::cout << " -> " << (td.variadic ? "variadic " : "")
33 << (correct ? termcolor::green : termcolor::magenta) << td.name << termcolor::reset << " (" << arg_str << ") ";
34 };
35
36 for (std::size_t i = 0, end = contract.arguments.size(); i < end; ++i)
37 {
38 const Typedef& td = contract.arguments[i];
39
40 if (td.variadic && i < args.size())
41 {
42 // variadic argument in contract and enough provided arguments
43 std::size_t bad_type = 0;
44 for (std::size_t j = i, args_end = args.size(); j < args_end; ++j)
45 {
46 if (td.types[0] != ValueType::Any && std::ranges::find(td.types, args[j].valueType()) == td.types.end())
47 bad_type++;
48 }
49
50 if (bad_type)
51 {
52 displayArg(td, false);
53 std::cout << termcolor::red << bad_type << termcolor::reset
54 << " argument" << (bad_type > 1 ? "s" : "") << " do not match";
55 }
56 else
57 displayArg(td, true);
58 }
59 else
60 {
61 // provided argument but wrong type
62 if (i < args.size() && td.types[0] != ValueType::Any && std::ranges::find(td.types, args[i].valueType()) == td.types.end())
63 {
64 displayArg(td, false);
65 std::cout << "was of type " << termcolor::red << types_to_str[static_cast<std::size_t>(args[i].valueType())];
66 }
67 // non-provided argument
68 else if (i >= args.size())
69 {
70 displayArg(td, false);
71 std::cout << termcolor::red << "was not provided";
72 }
73 else
74 displayArg(td, true);
75 }
76 std::cout << termcolor::reset << "\n";
77 }
78 }
79
80 [[noreturn]] void generateError(std::string_view funcname, const std::vector<Contract>& contracts, const std::vector<Value>& args)
81 {
82 std::cout << "Function " << termcolor::blue << funcname << termcolor::reset << " expected ";
83
84 std::vector<Value> sanitizedArgs;
85 std::ranges::copy_if(args, std::back_inserter(sanitizedArgs), [](const Value& value) -> bool {
86 return value.valueType() != ValueType::Undefined;
87 });
88
89 // get expected arguments count
90 std::size_t min_argc = std::numeric_limits<std::size_t>::max(), max_argc = 0;
91 for (const auto& [arguments] : contracts)
92 {
93 if (arguments.size() < min_argc)
94 min_argc = arguments.size();
95 if (arguments.size() > max_argc)
96 max_argc = arguments.size();
97 }
98
99 bool correct_argcount = true;
100
101 if (min_argc != max_argc)
102 {
103 std::cout << "between "
104 << termcolor::yellow << min_argc << termcolor::reset
105 << " argument" << (min_argc > 1 ? "s" : "") << " and "
106 << termcolor::yellow << max_argc << termcolor::reset
107 << " argument" << (max_argc > 1 ? "s" : "");
108
109 if (sanitizedArgs.size() < min_argc || sanitizedArgs.size() > max_argc)
110 correct_argcount = false;
111 }
112 else
113 {
114 std::cout << termcolor::yellow << min_argc << termcolor::reset
115 << " argument" << (min_argc > 1 ? "s" : "");
116
117 if (sanitizedArgs.size() != min_argc)
118 correct_argcount = false;
119 }
120
121 if (!correct_argcount)
122 std::cout << " but got " << termcolor::red << sanitizedArgs.size();
123
124 std::cout << termcolor::reset << "\n";
125
126 displayContract(contracts[0], sanitizedArgs);
127 for (std::size_t i = 1, end = contracts.size(); i < end; ++i)
128 {
129 std::cout << "Alternative " << (i + 1) << ":\n";
130 displayContract(contracts[i], sanitizedArgs);
131 }
132
133 throw TypeError("");
134 }
135}
ArkScript homemade exceptions.
A type error triggered when types don't match.
Definition: Exceptions.hpp:45
ValueType valueType() const noexcept
Definition: Value.hpp:118
ARK_API void generateError(std::string_view funcname, const std::vector< Contract > &contracts, const std::vector< Value > &args)
Generate an error message based on a given set of types contracts provided argument list.
Definition: TypeChecker.cpp:80
void displayContract(const Contract &contract, const std::vector< Value > &args)
Definition: TypeChecker.cpp:27
std::string typeListToString(const std::vector< ValueType > &types)
Definition: TypeChecker.cpp:11
@ Any
Used only for typechecking.
const std::array< std::string, 13 > types_to_str
Definition: Value.hpp:53
A contract is a list of typed arguments that a function can follow.
Definition: TypeChecker.hpp:83
std::vector< Typedef > arguments
Definition: TypeChecker.hpp:84
A type definition within a contract.
Definition: TypeChecker.hpp:64
std::string_view name
Definition: TypeChecker.hpp:65
std::vector< ValueType > types
Definition: TypeChecker.hpp:66