Skip to content

Commit

Permalink
feat(compiler): the compiler now outputs an IR instead of bytecode, w…
Browse files Browse the repository at this point in the history
…hich is now generated by the IRCompiler
  • Loading branch information
SuperFola committed Oct 6, 2024
1 parent a1353db commit e77cfbe
Show file tree
Hide file tree
Showing 8 changed files with 509 additions and 260 deletions.
105 changes: 44 additions & 61 deletions include/Ark/Compiler/Compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@

#include <Ark/Platform.hpp>
#include <Ark/Compiler/Instructions.hpp>
#include <Ark/Compiler/Word.hpp>
#include <Ark/Compiler/IntermediateRepresentation/Entity.hpp>
#include <Ark/Compiler/AST/Node.hpp>
#include <Ark/Compiler/ValTableElem.hpp>

namespace Ark
namespace Ark::internal
{
class State;
class Welder;
Expand All @@ -32,7 +32,7 @@ namespace Ark
* @brief The ArkScript bytecode compiler
*
*/
class ARK_API Compiler
class ARK_API Compiler final
{
public:
/**
Expand All @@ -47,17 +47,23 @@ namespace Ark
*
* @param ast
*/
void process(const internal::Node& ast);
void process(const Node& ast);

/**
* @brief Return the constructed bytecode object
* @brief Return the IR blocks (one per scope)
*
* @return const bytecode_t&
* @return const std::vector<Block>&
*/
[[nodiscard]] const bytecode_t& bytecode() const noexcept;
[[nodiscard]] const std::vector<IR::Block>& intermediateRepresentation() const noexcept;

friend class State;
friend class Welder;
[[nodiscard]] const std::vector<std::string>& symbols() const noexcept;

/**
* @brief Return the value table pre-computed
*
* @return const std::vector<ValTableElem>&
*/
[[nodiscard]] const std::vector<ValTableElem>& values() const noexcept;

private:
struct Page
Expand All @@ -67,34 +73,20 @@ namespace Ark
};

// tables: symbols, values, plugins and codes
std::vector<internal::Node> m_symbols;
std::vector<std::string> m_plugins;
std::vector<internal::ValTableElem> m_values;
std::vector<std::vector<internal::Word>> m_code_pages;
std::vector<std::vector<internal::Word>> m_temp_pages; ///< we need temporary code pages for some compilations passes
std::vector<std::string> m_symbols;
std::vector<ValTableElem> m_values;
std::vector<IR::Block> m_code_pages;
std::vector<IR::Block> m_temp_pages; ///< we need temporary code pages for some compilations passes

bytecode_t m_bytecode;
unsigned m_debug; ///< the debug level of the compiler

/**
* @brief Push the file headers (magic, version used, timestamp)
*
*/
void pushFileHeader() noexcept;

/**
* @brief Push the symbols and values tables
*
*/
void pushSymAndValTables();

/**
* @brief helper functions to get a temp or finalized code page
*
* @param page page descriptor
* @return std::vector<internal::Word>&
* @return std::vector<IR::Block>&
*/
std::vector<internal::Word>& page(const Page page) noexcept
IR::Block& page(const Page page) noexcept
{
if (!page.is_temp)
return m_code_pages[page.index];
Expand All @@ -105,9 +97,9 @@ namespace Ark
* @brief helper functions to get a temp or finalized code page
*
* @param page page descriptor
* @return std::vector<internal::Word>*
* @return std::vector<IR::Block>*
*/
std::vector<internal::Word>* page_ptr(const Page page) noexcept
IR::Block* page_ptr(const Page page) noexcept
{
if (!page.is_temp)
return &m_code_pages[page.index];
Expand All @@ -118,9 +110,9 @@ namespace Ark
* @brief Checking if a symbol is an operator
*
* @param name symbol name
* @return std::optional<uint8_t> operator instruction
* @return std::optional<Instruction> operator instruction
*/
static std::optional<uint8_t> getOperator(const std::string& name) noexcept;
static std::optional<Instruction> getOperator(const std::string& name) noexcept;

/**
* @brief Checking if a symbol is a builtin
Expand All @@ -134,17 +126,17 @@ namespace Ark
* @brief Checking if a symbol is a list instruction
*
* @param name
* @return std::optional<internal::Instruction> list instruction
* @return std::optional<Instruction> list instruction
*/
static std::optional<internal::Instruction> getListInstruction(const std::string& name) noexcept;
static std::optional<Instruction> getListInstruction(const std::string& name) noexcept;

/**
* Checks if a node is a list and has a keyboard as its first node, indicating if it's producing a value on the stack or not
* @param node node to check
* @return true if the node produces an output on the stack (fun, if, begin)
* @return false otherwise (let, mut, set, while, import, del)
*/
static bool nodeProducesOutput(const internal::Node& node);
static bool nodeProducesOutput(const Node& node);

/**
* @brief Check if a given instruction is unary (takes only one argument)
Expand All @@ -153,52 +145,43 @@ namespace Ark
* @return true the instruction is unary
* @return false
*/
static bool isUnaryInst(internal::Instruction inst) noexcept;

/**
* @brief Checking if a symbol may be coming from a plugin
*
* @param name symbol name
* @return true the symbol may be from a plugin, loaded at runtime
* @return false
*/
bool mayBeFromPlugin(const std::string& name) noexcept;
static bool isUnaryInst(Instruction inst) noexcept;

/**
* @brief Display a warning message
*
* @param message
* @param node
*/
static void compilerWarning(const std::string& message, const internal::Node& node);
static void compilerWarning(const std::string& message, const Node& node);

/**
* @brief Throw a nice error message
*
* @param message
* @param node
*/
[[noreturn]] static void throwCompilerError(const std::string& message, const internal::Node& node);
[[noreturn]] static void throwCompilerError(const std::string& message, const Node& node);

/**
* @brief Compile an expression (a node) recursively
*
* @param x the internal::Node to compile
* @param x the Node to compile
* @param p the current page number we're on
* @param is_result_unused
* @param is_terminal
* @param var_name
*/
void compileExpression(const internal::Node& x, Page p, bool is_result_unused, bool is_terminal, const std::string& var_name = "");
void compileExpression(const Node& x, Page p, bool is_result_unused, bool is_terminal, const std::string& var_name = "");

void compileSymbol(const internal::Node& x, Page p, bool is_result_unused);
void compileListInstruction(const internal::Node& c0, const internal::Node& x, Page p, bool is_result_unused);
void compileIf(const internal::Node& x, Page p, bool is_result_unused, bool is_terminal, const std::string& var_name);
void compileFunction(const internal::Node& x, Page p, bool is_result_unused, const std::string& var_name);
void compileLetMutSet(internal::Keyword n, const internal::Node& x, Page p);
void compileWhile(const internal::Node& x, Page p);
void compilePluginImport(const internal::Node& x, Page p);
void handleCalls(const internal::Node& x, Page p, bool is_result_unused, bool is_terminal, const std::string& var_name);
void compileSymbol(const Node& x, Page p, bool is_result_unused);
void compileListInstruction(const Node& c0, const Node& x, Page p, bool is_result_unused);
void compileIf(const Node& x, Page p, bool is_result_unused, bool is_terminal, const std::string& var_name);
void compileFunction(const Node& x, Page p, bool is_result_unused, const std::string& var_name);
void compileLetMutSet(Keyword n, const Node& x, Page p);
void compileWhile(const Node& x, Page p);
void compilePluginImport(const Node& x, Page p);
void handleCalls(const Node& x, Page p, bool is_result_unused, bool is_terminal, const std::string& var_name);

/**
* @brief Register a given node in the symbol table
Expand All @@ -207,7 +190,7 @@ namespace Ark
* @param sym
* @return uint16_t
*/
uint16_t addSymbol(const internal::Node& sym);
uint16_t addSymbol(const Node& sym);

/**
* @brief Register a given node in the value table
Expand All @@ -216,7 +199,7 @@ namespace Ark
* @param x
* @return uint16_t
*/
uint16_t addValue(const internal::Node& x);
uint16_t addValue(const Node& x);

/**
* @brief Register a page id (function reference) in the value table
Expand All @@ -226,7 +209,7 @@ namespace Ark
* @param current A reference to the current node, for context
* @return std::size_t
*/
uint16_t addValue(std::size_t page_id, const internal::Node& current);
uint16_t addValue(std::size_t page_id, const Node& current);
};
}

Expand Down
66 changes: 66 additions & 0 deletions include/Ark/Compiler/IntermediateRepresentation/Entity.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* @file Entity.hpp
* @author Alexandre Plateau ([email protected])
* @brief An entity in the IR is a bundle of information
* @version 0.1
* @date 2024-10-05
*
* @copyright Copyright (c) 2024
*
*/

#ifndef ARK_COMPILER_INTERMEDIATEREPRESENTATION_ENTITY_HPP
#define ARK_COMPILER_INTERMEDIATEREPRESENTATION_ENTITY_HPP

#include <cinttypes>
#include <vector>

#include <Ark/Compiler/Word.hpp>
#include <Ark/Compiler/Instructions.hpp>

namespace Ark::internal::IR
{
enum class Kind
{
Label,
Goto,
GotoIfTrue,
GotoIfFalse,
Opcode
};

using label_t = std::size_t;

class Entity
{
public:
explicit Entity(Kind kind);

explicit Entity(Instruction inst, uint16_t arg = 0);

static Entity Label();

static Entity Goto(const Entity& label);

static Entity GotoIf(const Entity& label, bool cond);

[[nodiscard]] Word bytecode() const;

[[nodiscard]] inline label_t label() const { return m_label; }

[[nodiscard]] inline Kind kind() const { return m_kind; }

private:
inline static label_t LabelCounter = 0;

Kind m_kind;
label_t m_label { 0 };
Instruction m_inst;
uint8_t m_secondary_arg { 0 };
uint16_t m_primary_arg { 0 };
};

using Block = std::vector<Entity>;
}

#endif // ARK_COMPILER_INTERMEDIATEREPRESENTATION_ENTITY_HPP
79 changes: 79 additions & 0 deletions include/Ark/Compiler/IntermediateRepresentation/IRCompiler.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* @file IRCompiler.hpp
* @author Alexandre Plateau ([email protected])
* @brief Compile the intermediate representation to bytecode
* @version 0.1
* @date 2024-10-05
*
* @copyright Copyright (c) 2024
*
*/

#ifndef ARK_COMPILER_INTERMEDIATEREPRESENTATION_IRCOMPILER_HPP
#define ARK_COMPILER_INTERMEDIATEREPRESENTATION_IRCOMPILER_HPP

#include <vector>
#include <string>

#include <Ark/Platform.hpp>
#include <Ark/Logger.hpp>
#include <Ark/Compiler/Common.hpp>
#include <Ark/Compiler/ValTableElem.hpp>
#include <Ark/Compiler/IntermediateRepresentation/Entity.hpp>

namespace Ark::internal
{
class ARK_API IRCompiler final
{
public:
/**
* @brief Create a new IRCompiler
*
* @param debug debug level
*/
explicit IRCompiler(unsigned debug);

/**
* @brief Turn a given IR into bytecode
*
* @param pages list of lists of IR entities generated by the compiler
* @param symbols symbol table generated by the compiler
* @param values value table generated by the compiler
*/
void process(const std::vector<IR::Block>& pages, const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values);

/**
* @brief Return the constructed bytecode object
*
* @return const bytecode_t&
*/
[[nodiscard]] const bytecode_t& bytecode() const noexcept;

private:
Logger m_logger;
bytecode_t m_bytecode;
std::vector<IR::Block> m_ir;

void compile();

/**
* @brief Push a word to the m_bytecode
* @param word
*/
void pushWord(const Word& word);

/**
* @brief Push the file headers (magic, version used, timestamp)
*
*/
void pushFileHeader() noexcept;

/**
* @brief Push the symbols and values tables
*
*/
void pushSymAndValTables(const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values);
};
}

#endif // ARK_COMPILER_INTERMEDIATEREPRESENTATION_IRCOMPILER_HPP
Loading

0 comments on commit e77cfbe

Please sign in to comment.