ArkScript
A small, fast, functional and scripting language for video games
main.cpp
Go to the documentation of this file.
1#include <cstdio>
2#include <iostream>
3#include <optional>
4#include <filesystem>
5#include <limits>
6
7#include <clipp.h>
8#include <termcolor/proxy.hpp>
9
10#include <Ark/Ark.hpp>
11#include <Ark/REPL/Repl.hpp>
12
13#include <Ark/Files.hpp>
15
16int main(int argc, char** argv)
17{
18 using namespace clipp;
19
20// TODO remove once next major version of ArkScript is available
21#if ARK_VERSION_MAJOR == 4
22# error "this code block should be removed from ArkScript 4.x.y"
23#endif
24 {
25 namespace fs = std::filesystem;
26 fs::path program(argv[0]);
27
28 if (program.stem() == "ark")
29 std::cout << termcolor::yellow << "Warning" << termcolor::reset << " the command `ark' is being deprecated in favor of `arkscript'" << std::endl;
30 }
31
32
33 enum class mode
34 {
35 help,
36 dev_info,
37 bytecode_reader,
38 version,
39 run,
40 repl,
41 compile,
42 eval,
43 ast
44 };
45 mode selected = mode::repl;
46 uint16_t options = Ark::DefaultFeatures;
47
48 std::string file = "",
49 eval_expresion = "";
50
51 unsigned debug = 0;
52
53 constexpr uint16_t max_uint16 = std::numeric_limits<uint16_t>::max();
54
55 uint16_t bcr_page = max_uint16;
56 uint16_t bcr_start = max_uint16;
57 uint16_t bcr_end = max_uint16;
59
60 std::vector<std::string> wrong, script_args;
61
62 std::string libdir = "";
63 std::vector<std::string> libenv;
64
65 // clang-format off
66 auto cli = (
67 option("-h", "--help").set(selected, mode::help).doc("Display this message")
68 | option("-v", "--version").set(selected, mode::version).doc("Display ArkScript version and exit")
69 | option("--dev-info").set(selected, mode::dev_info).doc("Display development information and exit")
70 | (
71 required("-e", "--eval").set(selected, mode::eval).doc("Evaluate ArkScript expression")
72 & value("expression", eval_expresion)
73 )
74 | (
75 required("-c", "--compile").set(selected, mode::compile).doc("Compile the given program to bytecode, but do not run")
76 & value("file", file)
77 , joinable(repeatable(option("-d", "--debug").call([&]{ debug++; }).doc("Increase debug level (default: 0)")))
78 )
79 | (
80 required("-bcr", "--bytecode-reader").set(selected, mode::bytecode_reader).doc("Launch the bytecode reader")
81 & value("file", file)
82 , (
83 option("-on", "--only-names").set(segment, Ark::BytecodeSegment::HeadersOnly).doc("Display only the bytecode segments names and sizes")
84 | (
85 (
86 option("-a", "--all").set(segment, Ark::BytecodeSegment::All).doc("Display all the bytecode segments (default)")
87 | option("-st", "--symbols").set(segment, Ark::BytecodeSegment::Symbols).doc("Display only the symbols table")
88 | option("-vt", "--values").set(segment, Ark::BytecodeSegment::Values).doc("Display only the values table")
89 )
90 , option("-s", "--slice").doc("Select a slice of instructions in the bytecode")
91 & value("start", bcr_start)
92 & value("end", bcr_end)
93 )
94 | (
95 option("-cs", "--code").set(segment, Ark::BytecodeSegment::Code).doc("Display only the code segments")
96 , option("-p", "--page").doc("Set the bytecode reader code segment to display")
97 & value("page", bcr_page)
98 )
99 )
100 )
101 | (
102 value("file", file).set(selected, mode::run)
103 , (
104 joinable(repeatable(option("-d", "--debug").call([&]{ debug++; })))
105 ,
106 // shouldn't change now, the lib option is fine and working
107 (
108 option("-L", "--lib").doc("Set the location of the ArkScript standard library. Paths can be delimited by ';'")
109 & value("lib_dir", libdir)
110 )
111 )
112 , any_other(script_args)
113 )
114 | (
115 required("--ast").set(selected, mode::ast).doc("Compile the given program and output its AST as JSON to stdout")
116 & value("file", file)
117 , joinable(repeatable(option("-d", "--debug").call([&]{ debug++; }).doc("Increase debug level (default: 0)")))
118 ,
119 (
120 option("-L", "--lib").doc("Set the location of the ArkScript standard library. Paths can be delimited by ';'")
121 & value("lib_dir", libdir)
122 )
123 )
124 , any_other(wrong)
125 );
126 // clang-format on
127
128 auto fmt = doc_formatting {}
129 .first_column(8) // column where usage lines and documentation starts
130 .doc_column(36) // parameter docstring start col
131 .indent_size(2) // indent of documentation lines for children of a documented group
132 .split_alternatives(true) // split usage into several lines for large alternatives
133 .merge_alternative_flags_with_common_prefix(true) // [-fok] [-fno-ok] becomes [-f(ok|no-ok)]
134 ;
135
136 if (parse(argc, argv, cli) && wrong.empty())
137 {
138 using namespace Ark;
139
140 if (!libdir.empty())
141 libenv = Utils::splitString(libdir, ';');
142
143 switch (selected)
144 {
145 case mode::help:
146 // clipp only supports streams
147 std::cout << make_man_page(cli, "arkscript", fmt)
148 .prepend_section("DESCRIPTION", " ArkScript programming language")
149 .append_section("LICENSE", " Mozilla Public License 2.0")
150 << std::endl;
151 break;
152
153 case mode::version:
154 std::printf("Version %i.%i.%i\n", ARK_VERSION_MAJOR, ARK_VERSION_MINOR, ARK_VERSION_PATCH);
155 break;
156
157 case mode::dev_info:
158 {
159 std::printf(
160 "Have been compiled with %s, options: %s\n\n"
161 "sizeof(Ark::Value) = %zuB\n"
162 " sizeof(Value_t) = %zuB\n"
163 " sizeof(ValueType) = %zuB\n"
164 " sizeof(ProcType) = %zuB\n"
165 " sizeof(Ark::Closure) = %zuB\n"
166 " sizeof(Ark::UserType) = %zuB\n"
167 "\nVirtual Machine\n"
168 "sizeof(Ark::VM) = %zuB\n"
169 " sizeof(Ark::State) = %zuB\n"
170 " sizeof(Ark::Scope) = %zuB\n"
171 " sizeof(ExecutionContext) = %zuB\n"
172 "\nMisc\n"
173 " sizeof(vector<Ark::Value>) = %zuB\n"
174 " sizeof(std::string) = %zuB\n"
175 " sizeof(String) = %zuB\n"
176 " sizeof(char) = %zuB\n"
177 "\nsizeof(Node) = %zuB\n",
179 // value
180 sizeof(Ark::Value),
181 sizeof(Ark::Value::Value_t),
182 sizeof(Ark::ValueType),
183 sizeof(Ark::Value::ProcType),
185 sizeof(Ark::UserType),
186 // vm
187 sizeof(Ark::VM),
188 sizeof(Ark::State),
189 sizeof(Ark::internal::Scope),
191 // misc
192 sizeof(std::vector<Ark::Value>),
193 sizeof(std::string),
194 sizeof(String),
195 sizeof(char),
196 sizeof(Ark::internal::Node));
197 break;
198 }
199
200 case mode::repl:
201 {
202 // send default features without FeatureRemoveUnusedVars to avoid deleting code which will be used later on
204 return repl.run();
205 }
206
207 case mode::compile:
208 {
209 Ark::State state(options, libenv);
210 state.setDebug(debug);
211
212 if (!state.doFile(file))
213 {
214 std::cerr << "Could not compile file at " << file << "\n";
215 return -1;
216 }
217
218 break;
219 }
220
221 case mode::run:
222 {
223 Ark::State state(options, libenv);
224 state.setDebug(debug);
225 state.setArgs(script_args);
226
227 if (!state.doFile(file))
228 {
229 std::cerr << "Could not run file at " << file << "\n";
230 return -1;
231 }
232
233 Ark::VM vm(state);
234 int out = vm.run();
235
236#ifdef ARK_PROFILER_COUNT
237 std::printf(
238 "\n\nValue\n"
239 "=====\n"
240 "\tCreations: %u\n\tCopies: %u\n\tMoves: %u\n\n\tCopy coeff: %f",
241 Ark::internal::value_creations,
242 Ark::internal::value_copies,
243 Ark::internal::value_moves,
244 static_cast<float>(Ark::internal::value_copies) / Ark::internal::value_creations);
245#endif
246
247 return out;
248 }
249
250 case mode::eval:
251 {
252 Ark::State state(options, libenv);
253 state.setDebug(debug);
254
255 if (!state.doString(eval_expresion))
256 {
257 std::cerr << "Could not evaluate expression\n";
258 return -1;
259 }
260
261 Ark::VM vm(state);
262 return vm.run();
263 }
264
265 case mode::ast:
266 {
267 Ark::JsonCompiler jcompiler(debug, libenv, options);
268 jcompiler.feed(Ark::Utils::readFile(file), file);
269 std::cout << jcompiler.compile() << std::endl;
270 break;
271 }
272
273 case mode::bytecode_reader:
274 {
275 try
276 {
278 bcr.feed(file);
279
280 if (bcr_page == max_uint16 && bcr_start == max_uint16)
281 bcr.display(segment);
282 else if (bcr_page != max_uint16 && bcr_start == max_uint16)
283 bcr.display(segment, std::nullopt, std::nullopt, bcr_page);
284 else if (bcr_page == max_uint16 && bcr_start != max_uint16)
285 bcr.display(segment, bcr_start, bcr_end);
286 else
287 bcr.display(segment, bcr_start, bcr_end, bcr_page);
288 }
289 catch (const std::exception& e)
290 {
291 std::printf("%s\n", e.what());
292 }
293 break;
294 }
295 }
296 }
297 else
298 {
299 for (const auto& arg : wrong)
300 std::printf("'%s' ins't a valid argument\n", arg.c_str());
301
302 // clipp only supports streams
303 std::cout << make_man_page(cli, "arkscript", fmt)
304 .prepend_section("DESCRIPTION", " ArkScript programming language")
305 .append_section("LICENSE", " Mozilla Public License 2.0")
306 << std::endl;
307 }
308
309 return 0;
310}
Includes the needed files to start using ArkScript.
#define ARK_COMPILER
Definition: Constants.hpp:24
constexpr int ARK_VERSION_MAJOR
Definition: Constants.hpp:16
constexpr int ARK_VERSION_PATCH
Definition: Constants.hpp:18
#define ARK_COMPILATION_OPTIONS
Definition: Constants.hpp:23
constexpr int ARK_VERSION_MINOR
Definition: Constants.hpp:17
Lots of utilities about the filesystem.
ArkScript REPL - Read Eval Print Loop.
This class is just a helper to.
void display(BytecodeSegment segment=BytecodeSegment::All, std::optional< uint16_t > sStart=std::nullopt, std::optional< uint16_t > sEnd=std::nullopt, std::optional< uint16_t > cPage=std::nullopt)
Display the bytecode opcode in a human friendly way.
void feed(const std::string &file)
Construct needed data before displaying information about a given file.
void feed(const std::string &code, const std::string &filename=ARK_NO_NAME_FILE)
Feed the differents variables with information taken from the given source code file.
std::string compile()
Start the compilation.
int run()
Start the REPL.
Definition: Repl.cpp:15
Ark state to handle the dirty job of loading and compiling ArkScript code.
Definition: State.hpp:31
void setArgs(const std::vector< std::string > &args) noexcept
Set the script arguments in sys:args.
Definition: State.cpp:181
void setDebug(unsigned level) noexcept
Set the debug level.
Definition: State.cpp:191
bool doFile(const std::string &filename)
Compile a file, and use the resulting bytecode.
Definition: State.cpp:109
bool doString(const std::string &code)
Compile a string (representing ArkScript code) and store resulting bytecode in m_bytecode.
Definition: State.cpp:151
A class to be use C++ objects in ArkScript.
Definition: UserType.hpp:50
The ArkScript virtual machine, executing ArkScript bytecode.
Definition: VM.hpp:48
int run() noexcept
Run the bytecode held in the state.
Definition: VM.cpp:245
Value(*)(std::vector< Value > &, VM *) ProcType
Definition: Value.hpp:73
std::variant< double, String, internal::PageAddr_t, ProcType, internal::Closure, UserType, std::vector< Value >, Value * > Value_t
Definition: Value.hpp:86
Closure management.
Definition: Closure.hpp:45
A node of an Abstract Syntax Tree for ArkScript.
Definition: Node.hpp:29
A class to handle the VM scope more efficiently.
Definition: Scope.hpp:28
int main(int argc, char **argv)
Definition: main.cpp:16
std::string readFile(const std::string &name)
Helper to read a file.
Definition: Files.hpp:48
Definition: Builtins.hpp:21
constexpr uint16_t DefaultFeatures
Definition: Constants.hpp:52
ValueType
Definition: Value.hpp:39
constexpr uint16_t FeatureRemoveUnusedVars
Definition: Constants.hpp:48