Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/ir #498

Merged
merged 10 commits into from
Oct 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/images/diagram.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ __arkscript__/
*.arkc
*.arkm
/*.ark
/*.ark.ir
!tests/unittests/resources/BytecodeReaderSuite/*.arkc

# Generated files
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
- the name & scope resolution pass now checks for mutability errors
- compile time checks for mutability errors with `append!`, `concat!` and `pop!`
- new `MAKE_CLOSURE <page addr>` instruction, generated in place of a `LOAD_CONST` when a closure is made
- added `-fdump-ir` to dump the IR entities to a file named `{file}.ark.ir`
- added 11 super instructions and their implementation to the VM

### Changed
- instructions are on 4 bytes: 1 byte for the instruction, 1 byte of padding, 2 bytes for an immediate argument
Expand Down Expand Up @@ -81,6 +83,9 @@
- the `STORE` instruction has been renamed `SET_VAL`
- the `STORE` instruction is emitted in place of the `LET` and `MUT` instructions, without any mutability checking now
- `io:writeFile` no longer takes a mode and has been split into `io:writeFile` and `io:appendToFile`
- instructions are now positioned like this: `inst byte1 byte2 byte3`
- byte1 is 0 if the instruction takes a single argument on 16 bits, split on byte2 and byte3
- if the instruction takes two arguments, they each have 12 bits ; the second one is on byte1 and upper half of byte2, the first on lower half of byte2 and then byte3

### Removed
- removed unused `NodeType::Closure`
Expand Down
40 changes: 0 additions & 40 deletions include/Ark/Compiler/AST/Predicates.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,26 +59,6 @@ namespace Ark::internal
}
} IsHex;

inline struct IsUpper final : CharPred
{
IsUpper() :
CharPred("uppercase") {}
bool operator()(const utf8_char_t::codepoint_t c) const override
{
return 0 <= c && c <= 255 && std::isupper(c) != 0;
}
} IsUpper;

inline struct IsLower final : CharPred
{
IsLower() :
CharPred("lowercase") {}
bool operator()(const utf8_char_t::codepoint_t c) const override
{
return 0 <= c && c <= 255 && std::islower(c) != 0;
}
} IsLower;

inline struct IsAlpha final : CharPred
{
IsAlpha() :
Expand All @@ -99,16 +79,6 @@ namespace Ark::internal
}
} IsAlnum;

inline struct IsPrint final : CharPred
{
IsPrint() :
CharPred("printable") {}
bool operator()(const utf8_char_t::codepoint_t c) const override
{
return 0 <= c && c <= 255 && std::isprint(c) != 0;
}
} IsPrint;

struct IsChar final : CharPred
{
explicit IsChar(const char c) :
Expand Down Expand Up @@ -186,16 +156,6 @@ namespace Ark::internal
}
} IsSymbol;

inline struct IsAny final : CharPred
{
IsAny() :
CharPred("any") {}
bool operator()(const utf8_char_t::codepoint_t) const override
{
return true;
}
} IsAny;

const IsChar IsMinus('-');
}

Expand Down
119 changes: 47 additions & 72 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,28 @@ 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;
/**
* @brief Return the symbol table pre-computed
*
* @return const std::vector<std::string>&
*/
[[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,60 +78,33 @@ 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];
return m_temp_pages[page.index];
}

/**
* @brief helper functions to get a temp or finalized code page
*
* @param page page descriptor
* @return std::vector<internal::Word>*
*/
std::vector<internal::Word>* page_ptr(const Page page) noexcept
{
if (!page.is_temp)
return &m_code_pages[page.index];
return &m_temp_pages[page.index];
}

/**
* @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 +118,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 +137,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 +182,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 +191,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 +201,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
Loading
Loading