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