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