ArkScript
A small, lisp-inspired, functional scripting language
Welder.cpp
Go to the documentation of this file.
1#include <Ark/Constants.hpp>
3
8#include <Ark/Utils/Files.hpp>
12
13#include <cassert>
14#include <sstream>
15#include <fmt/ostream.h>
16
17namespace Ark
18{
19 Welder::Welder(const unsigned debug, const std::vector<std::filesystem::path>& lib_env, const uint16_t features) :
20 m_lib_env(lib_env),
21 m_features(features),
22 m_computed_ast(internal::NodeType::Unused),
23 m_parser(debug),
24 m_import_solver(debug, lib_env),
25 m_macro_processor(debug),
26 m_ast_optimizer(debug),
27 m_name_resolver(debug),
28 m_logger("Welder", debug),
29 m_lowerer(debug),
30 m_ir_inliner(debug),
31 m_ir_optimizer(debug),
32 m_ir_compiler(debug)
33 {}
34
35 void Welder::registerSymbol(const std::string& name)
36 {
37 m_name_resolver.addDefinedSymbol(name, /* is_mutable= */ false);
38 }
39
40 bool Welder::computeASTFromFile(const std::string& filename)
41 {
42 m_root_file = std::filesystem::path(filename);
43 const std::string code = Utils::readFile(filename);
44
45 return computeAST(filename, code);
46 }
47
48 bool Welder::computeASTFromString(const std::string& code)
49 {
50 m_root_file = std::nullopt;
51 return computeAST(ARK_NO_NAME_FILE, code);
52 }
53
54 bool Welder::computeASTFromStringWithKnownSymbols(const std::string& code, const std::vector<std::string>& symbols)
55 {
56 m_root_file = std::nullopt;
57
58 for (const std::string& sym : symbols)
59 m_name_resolver.addDefinedSymbol(sym, /* is_mutable= */ true);
60 return computeAST(ARK_NO_NAME_FILE, code);
61 }
62
99
100 bool Welder::generateBytecodeUsingTables(const std::vector<std::string>& symbols, const std::vector<Value>& constants, const std::size_t start_page_at_offset)
101 {
102 std::vector<internal::ValTableElem> values;
103 for (const Value& constant : constants)
104 {
105 switch (constant.valueType())
106 {
108 values.emplace_back(constant.number());
109 break;
110
112 values.emplace_back(constant.string());
113 break;
114
116 values.emplace_back(static_cast<std::size_t>(constant.pageAddr()));
117 break;
118
119 default:
120 assert(false && "This should not be possible to have a constant that isn't a Number, a String or a PageAddr");
121 break;
122 }
123 }
124
125 m_lowerer.addToTables(symbols, values);
126 m_lowerer.offsetPagesBy(start_page_at_offset);
127 return generateBytecode();
128 }
129
130 bool Welder::saveBytecodeToFile(const std::string& filename)
131 {
132 m_logger.info("Final bytecode size: {}B", m_bytecode.size() * sizeof(uint8_t));
133
134 if (m_bytecode.empty())
135 return false;
136
137 std::ofstream output(filename, std::ofstream::binary);
138 output.write(
139 reinterpret_cast<char*>(&m_bytecode[0]),
140 static_cast<std::streamsize>(m_bytecode.size() * sizeof(uint8_t)));
141 output.close();
142 return true;
143 }
144
157
158 const internal::Node& Welder::ast() const noexcept
159 {
160 return m_computed_ast;
161 }
162
163 std::string Welder::textualIR() const noexcept
164 {
165 std::stringstream stream;
167 return stream.str();
168 }
169
170 const bytecode_t& Welder::bytecode() const noexcept
171 {
172 return m_bytecode;
173 }
174
176 {
177 std::filesystem::path path = m_root_file.value_or(std::filesystem::current_path());
178 if (is_directory(path))
179 path = path / ARK_CACHE_DIRNAME / "output.ark.ir";
180 else
181 {
182 const auto filename = path.filename().replace_extension(".ark.ir");
183 path.remove_filename();
184 path = path / ARK_CACHE_DIRNAME / filename;
185 }
186
187 std::ofstream output(path);
189 output.close();
190 }
191
192 bool Welder::computeAST(const std::string& filename, const std::string& code)
193 {
194 try
195 {
196 m_parser.process(filename, code);
198
199 if ((m_features & FeatureImportSolver) != 0)
200 {
201 m_import_solver.setup(m_root_file.value_or(std::filesystem::current_path()), m_parser.imports());
204 }
205
207 {
210 }
211
212 if ((m_features & FeatureNameResolver) != 0)
213 {
216 }
217
218 if ((m_features & FeatureASTOptimiser) != 0)
219 {
222 }
223
224 return true;
225 }
226 catch (const CodeError& e)
227 {
229 throw;
230
231 if (filename != ARK_NO_NAME_FILE && Utils::fileExists(filename) && std::filesystem::is_regular_file(filename))
233 else
235 return false;
236 }
237 }
238}
Constants used by ArkScript.
#define ARK_NO_NAME_FILE
Definition Constants.hpp:34
#define ARK_CACHE_DIRNAME
Definition Constants.hpp:33
Tools to report code errors nicely to the user.
ArkScript homemade exceptions.
Lots of utilities about the filesystem.
Handle imports, resolve them with modules and everything.
Resolves names and fully qualify them in the AST (prefixing them with the package they are from)
Optimizes a given ArkScript AST.
Handles the macros and their expansion in ArkScript source code.
Default value type handled by the virtual machine.
In charge of welding everything needed to compile code.
bool computeAST(const std::string &filename, const std::string &code)
Definition Welder.cpp:192
internal::ImportSolver m_import_solver
Definition Welder.hpp:125
internal::Node m_computed_ast
Definition Welder.hpp:122
Welder(unsigned debug, const std::vector< std::filesystem::path > &lib_env, uint16_t features=DefaultFeatures)
Create a new Welder.
Definition Welder.cpp:19
internal::IROptimizer m_ir_optimizer
Definition Welder.hpp:133
void registerSymbol(const std::string &name)
Register a symbol as a global in the compiler.
Definition Welder.cpp:35
bool computeASTFromString(const std::string &code)
Definition Welder.cpp:48
std::string textualIR() const noexcept
Definition Welder.cpp:163
internal::Logger m_logger
Definition Welder.hpp:130
bool generateBytecodeUsingTables(const std::vector< std::string > &symbols, const std::vector< Value > &constants, std::size_t start_page_at_offset)
Compile the AST processed by computeASTFromFile / computeASTFromString, with prefilled symbols and co...
Definition Welder.cpp:100
std::vector< internal::IR::Block > m_ir
Definition Welder.hpp:120
internal::NameResolutionPass m_name_resolver
Definition Welder.hpp:128
internal::IRInliner m_ir_inliner
Definition Welder.hpp:132
internal::ASTLowerer m_lowerer
Definition Welder.hpp:131
std::optional< std::filesystem::path > m_root_file
Definition Welder.hpp:118
const bytecode_t & bytecode() const noexcept
Definition Welder.cpp:170
internal::Parser m_parser
Definition Welder.hpp:124
internal::IRCompiler m_ir_compiler
Definition Welder.hpp:134
internal::MacroProcessor m_macro_processor
Definition Welder.hpp:126
void redirectLogsTo(std::ostream &os)
Redirect the logs to a given stream.
Definition Welder.cpp:145
bool saveBytecodeToFile(const std::string &filename)
Save the generated bytecode to a given file.
Definition Welder.cpp:130
bool generateBytecode()
Compile the AST processed by computeASTFromFile / computeASTFromString.
Definition Welder.cpp:63
bool computeASTFromFile(const std::string &filename)
Definition Welder.cpp:40
uint16_t m_features
Definition Welder.hpp:116
internal::Optimizer m_ast_optimizer
Definition Welder.hpp:127
bool computeASTFromStringWithKnownSymbols(const std::string &code, const std::vector< std::string > &symbols)
Compile code from a string, with a set of known symbols (useful for the debugger)
Definition Welder.cpp:54
void dumpIRToFile() const
Definition Welder.cpp:175
bytecode_t m_bytecode
Definition Welder.hpp:121
const internal::Node & ast() const noexcept
Definition Welder.cpp:158
IR::label_t lastLabel() const noexcept
const std::vector< ValTableElem > & values() const noexcept
Return the value table pre-computed.
void process(Node &ast)
Start the compilation.
void offsetPagesBy(std::size_t offset)
Start bytecode pages at a given offset (by default, 0)
const std::vector< IR::Block > & intermediateRepresentation() const noexcept
Return the IR blocks (one per scope)
const std::vector< std::string > & symbols() const noexcept
Return the symbol table pre-computed.
void addToTables(const std::vector< std::string > &symbols, const std::vector< ValTableElem > &constants)
Pre-fill tables (used by the debugger)
void dumpToStream(std::ostream &stream) const
Dump the IR given to process to an output stream.
const bytecode_t & bytecode() const noexcept
Return the constructed bytecode object.
void process(const std::vector< IR::Block > &pages, const std::vector< std::string > &symbols, const std::vector< ValTableElem > &values)
Turn a given IR into bytecode.
void process(const std::vector< IR::Block > &pages, const std::vector< std::string > &symbols, const std::vector< ValTableElem > &values, IR::label_t last_label)
Attempt to inline IR blocks to avoid function calls when possible.
Definition IRInliner.cpp:14
const std::vector< IR::Block > & intermediateRepresentation() const noexcept
Return the IR blocks (one per scope)
Definition IRInliner.cpp:81
const std::vector< IR::Block > & intermediateRepresentation() const noexcept
Return the IR blocks (one per scope)
void process(const std::vector< IR::Block > &pages, const std::vector< std::string > &symbols, const std::vector< ValTableElem > &values)
Turn a given IR into bytecode.
const Node & ast() const noexcept
ImportSolver & setup(const std::filesystem::path &root, const std::vector< Import > &origin_imports)
Configure the ImportSolver.
void process(const Node &origin_ast)
void info(const char *fmt, Args &&... args)
Write an info level log using fmtlib.
Definition Logger.hpp:63
void configureOutputStream(std::ostream *os)
Set a custom output stream to use for warnings. This will disable colors.
Definition Logger.hpp:147
const Node & ast() const noexcept
Return the modified AST.
Definition Processor.cpp:45
void process(const Node &ast)
Send the complete AST and work on it.
Definition Processor.cpp:30
const Node & ast() const noexcept
Unused overload that return the input AST (untouched as this pass only generates errors)
void process(const Node &ast)
Start visiting the given AST, checking for mutability violation and unbound variables.
std::string addDefinedSymbol(const std::string &sym, bool is_mutable)
Register a symbol as defined, so that later we can throw errors on undefined symbols.
A node of an Abstract Syntax Tree for ArkScript.
Definition Node.hpp:32
const Node & ast() const noexcept
Returns the modified AST.
Definition Optimizer.cpp:30
void process(const Node &ast)
Send the AST to the optimizer, then run the different optimization strategies on it.
Definition Optimizer.cpp:11
void process(const std::string &filename, const std::string &code)
Parse the given code.
Definition Parser.cpp:51
const Node & ast() const noexcept
Definition Parser.cpp:92
const std::vector< Import > & imports() const
Definition Parser.cpp:97
void configureLogger(std::ostream &os)
Set a custom output stream for the logger.
Definition Pass.cpp:10
ARK_API void generateWithCode(const CodeError &e, const std::string &code, std::ostream &os=std::cerr, bool colorize=true)
ARK_API void generate(const CodeError &e, std::ostream &os=std::cerr, bool colorize=true)
Generate a diagnostic from an error and print it to the standard error output.
std::string readFile(const std::string &name)
Helper to read a file.
Definition Files.hpp:47
bool fileExists(const std::string &name) noexcept
Checks if a file exists.
Definition Files.hpp:28
NodeType
The different node types available.
Definition Common.hpp:44
constexpr uint16_t FeatureImportSolver
Definition Constants.hpp:60
constexpr uint16_t FeatureASTOptimiser
Disabled by default because embedding ArkScript should not prune nodes from the AST ; it is active in...
Definition Constants.hpp:62
constexpr uint16_t FeatureIROptimiser
Definition Constants.hpp:63
constexpr uint16_t FeatureMacroProcessor
Definition Constants.hpp:61
constexpr uint16_t FeatureTestFailOnException
This feature should only be used in tests, to disable diagnostics generation and enable exceptions to...
Definition Constants.hpp:71
constexpr uint16_t FeatureIRInliner
Definition Constants.hpp:64
std::vector< uint8_t > bytecode_t
Definition Common.hpp:22
constexpr uint16_t FeatureDumpIR
Definition Constants.hpp:69
constexpr uint16_t FeatureNameResolver
Definition Constants.hpp:65
CodeError thrown by the compiler (parser, macro processor, optimizer, and compiler itself)