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