2022-04-24 15:27:09 +02:00
|
|
|
#pragma once
|
|
|
|
|
2022-05-02 20:06:40 +02:00
|
|
|
#include <concepts>
|
2022-04-24 15:27:09 +02:00
|
|
|
#include <cstdint>
|
2022-05-02 20:06:40 +02:00
|
|
|
#include <cstring>
|
2022-05-02 14:50:04 +02:00
|
|
|
#include <optional>
|
2022-04-24 15:27:09 +02:00
|
|
|
#include <ostream>
|
2022-05-07 20:52:09 +02:00
|
|
|
#include <span>
|
2022-04-27 14:58:02 +02:00
|
|
|
#include <string_view>
|
2022-04-24 15:27:09 +02:00
|
|
|
#include <tl/expected.hpp>
|
2022-05-02 14:50:04 +02:00
|
|
|
#include <variant>
|
2022-04-24 15:27:09 +02:00
|
|
|
|
2022-05-08 00:03:22 +02:00
|
|
|
#if defined(__cpp_lib_source_location)
|
|
|
|
#include <source_location>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// To make sure, that we don't collide with <cassert> macro
|
|
|
|
#ifdef assert
|
|
|
|
#undef assert
|
|
|
|
#endif
|
|
|
|
|
2022-04-24 15:27:09 +02:00
|
|
|
using u8 = std::uint8_t;
|
|
|
|
using u16 = std::uint16_t;
|
|
|
|
using u32 = std::uint32_t;
|
|
|
|
using u64 = std::uint64_t;
|
|
|
|
|
|
|
|
using i8 = std::int8_t;
|
|
|
|
using i16 = std::int16_t;
|
|
|
|
using i32 = std::int32_t;
|
|
|
|
using i64 = std::int64_t;
|
|
|
|
|
2022-04-27 13:48:50 +02:00
|
|
|
using usize = std::size_t;
|
|
|
|
using isize = std::ptrdiff_t;
|
2022-04-24 16:09:55 +02:00
|
|
|
|
2022-05-07 20:52:09 +02:00
|
|
|
// Error handling mechanism inspired by Andrew Kelly approach, that was implemented
|
|
|
|
// as first class feature in Zig programming language.
|
2022-04-24 16:09:55 +02:00
|
|
|
namespace errors
|
|
|
|
{
|
|
|
|
enum Type
|
|
|
|
{
|
2022-04-27 14:37:21 +02:00
|
|
|
End_Of_File,
|
2022-05-07 20:52:09 +02:00
|
|
|
Unrecognized_Character,
|
|
|
|
|
|
|
|
Unexpected_Token_Type,
|
|
|
|
Unexpected_Empty_Source,
|
2022-05-16 00:06:27 +02:00
|
|
|
Failed_Numeric_Parsing,
|
2022-05-16 02:18:53 +02:00
|
|
|
|
2022-05-16 15:51:38 +02:00
|
|
|
Function_Not_Defined,
|
2022-05-16 16:58:31 +02:00
|
|
|
Unresolved_Operator,
|
|
|
|
Expected_Keyword,
|
2022-05-17 02:35:51 +02:00
|
|
|
Not_Callable
|
2022-04-24 16:09:55 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-05-08 18:02:23 +02:00
|
|
|
/// \brief Location describes code position in `file line column` format.
|
|
|
|
/// It's used both to represent position in source files provided
|
|
|
|
// to interpreter and internal interpreter usage.
|
2022-04-27 14:58:02 +02:00
|
|
|
struct Location
|
|
|
|
{
|
2022-05-08 18:02:23 +02:00
|
|
|
std::string_view filename = "<unnamed>"; ///< File that location is pointing to
|
|
|
|
usize line = 1; ///< Line number (1 based) that location is pointing to
|
|
|
|
usize column = 1; ///< Column number (1 based) that location is pointing to
|
2022-04-27 14:58:02 +02:00
|
|
|
|
2022-05-08 18:02:23 +02:00
|
|
|
/// Advances line and column numbers based on provided rune
|
|
|
|
///
|
|
|
|
/// If rune is newline, then column is reset to 1, and line number is incremented.
|
|
|
|
/// Otherwise column number is incremented.
|
|
|
|
///
|
|
|
|
/// @param rune Rune from which column and line numbers advancements are made.
|
|
|
|
Location& advance(u32 rune);
|
2022-04-27 14:58:02 +02:00
|
|
|
|
|
|
|
bool operator==(Location const& rhs) const = default;
|
|
|
|
|
2022-05-08 18:02:23 +02:00
|
|
|
//! Creates location at default filename with specified line and column number
|
|
|
|
static Location at(usize line, usize column);
|
2022-05-08 00:03:22 +02:00
|
|
|
|
|
|
|
// Used to describe location of function call in interpreter (internal use only)
|
|
|
|
#if defined(__cpp_lib_source_location)
|
|
|
|
static Location caller(std::source_location loc = std::source_location::current());
|
|
|
|
#elif (__has_builtin(__builtin_FILE) and __has_builtin(__builtin_LINE))
|
|
|
|
static Location caller(char const* file = __builtin_FILE(), usize line = __builtin_LINE());
|
|
|
|
#else
|
|
|
|
#error Cannot implement Location::caller function
|
2022-05-08 18:02:23 +02:00
|
|
|
/// Returns location of call in interpreter source code.
|
|
|
|
///
|
|
|
|
/// Example of reporting where `foo()` was beeing called:
|
|
|
|
/// @code
|
|
|
|
/// void foo(Location loc = Location::caller()) { std::cout << loc << '\n'; }
|
|
|
|
/// @endcode
|
|
|
|
static Location caller();
|
2022-05-08 00:03:22 +02:00
|
|
|
#endif
|
2022-04-27 14:58:02 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
std::ostream& operator<<(std::ostream& os, Location const& location);
|
|
|
|
|
2022-05-08 00:03:22 +02:00
|
|
|
void assert(bool condition, std::string message, Location loc = Location::caller());
|
|
|
|
|
|
|
|
// Marks part of code that was not implemented yet
|
|
|
|
[[noreturn]] void unimplemented(Location loc = Location::caller());
|
|
|
|
|
|
|
|
// Marks location that should not be reached
|
|
|
|
[[noreturn]] void unreachable(Location loc = Location::caller());
|
|
|
|
|
2022-04-24 15:27:09 +02:00
|
|
|
struct Error
|
|
|
|
{
|
2022-04-24 16:09:55 +02:00
|
|
|
errors::Type type;
|
2022-05-02 14:50:04 +02:00
|
|
|
std::optional<Location> location = std::nullopt;
|
2022-05-07 20:52:09 +02:00
|
|
|
std::string message{};
|
2022-05-16 00:06:27 +02:00
|
|
|
std::errc error_code{};
|
2022-04-24 16:09:55 +02:00
|
|
|
|
|
|
|
bool operator==(errors::Type);
|
2022-05-07 20:52:09 +02:00
|
|
|
Error with(Location) &&;
|
2022-04-24 15:27:09 +02:00
|
|
|
};
|
|
|
|
|
2022-05-08 00:03:22 +02:00
|
|
|
std::ostream& operator<<(std::ostream& os, Error const& err);
|
|
|
|
|
2022-05-17 02:35:51 +02:00
|
|
|
template<template<typename ...> typename Template, typename>
|
|
|
|
struct is_template : std::false_type {};
|
|
|
|
|
|
|
|
template<template<typename ...> typename Template, typename ...T>
|
|
|
|
struct is_template<Template, Template<T...>> : std::true_type {};
|
|
|
|
|
|
|
|
template<template<typename ...> typename Template, typename T>
|
|
|
|
constexpr auto is_template_v = is_template<Template, T>::value;
|
|
|
|
|
2022-04-24 15:27:09 +02:00
|
|
|
template<typename T>
|
2022-05-08 15:57:31 +02:00
|
|
|
struct [[nodiscard("This value may contain critical error, so it should NOT be ignored")]] Result : tl::expected<T, Error>
|
2022-04-27 13:48:50 +02:00
|
|
|
{
|
2022-05-02 15:13:12 +02:00
|
|
|
using Storage = tl::expected<T, Error>;
|
|
|
|
|
2022-05-08 00:03:22 +02:00
|
|
|
constexpr Result() = default;
|
2022-04-27 13:48:50 +02:00
|
|
|
|
2022-05-08 00:03:22 +02:00
|
|
|
template<typename ...Args> requires (not std::is_void_v<T>) && std::is_constructible_v<T, Args...>
|
|
|
|
constexpr Result(Args&& ...args)
|
|
|
|
: Storage( T{ std::forward<Args>(args)... } )
|
2022-05-02 15:13:12 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-05-08 00:03:22 +02:00
|
|
|
template<typename Arg> requires std::is_constructible_v<Storage, Arg>
|
|
|
|
constexpr Result(Arg &&arg)
|
|
|
|
: Storage(std::forward<Arg>(arg))
|
2022-04-27 13:48:50 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-05-08 00:03:22 +02:00
|
|
|
constexpr Result(errors::Type error)
|
|
|
|
: Storage(tl::unexpect, Error { error } )
|
2022-05-07 20:52:09 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-05-08 00:03:22 +02:00
|
|
|
inline Result(Error error)
|
|
|
|
: Storage(tl::unexpected(std::move(error)))
|
2022-04-27 13:48:50 +02:00
|
|
|
{
|
|
|
|
}
|
2022-04-24 15:27:09 +02:00
|
|
|
|
2022-05-08 00:03:22 +02:00
|
|
|
// Internal function used for definition of Try macro
|
|
|
|
inline auto value() &&
|
|
|
|
{
|
|
|
|
if constexpr (not std::is_void_v<T>) {
|
|
|
|
return Storage::value();
|
|
|
|
}
|
|
|
|
}
|
2022-05-17 02:35:51 +02:00
|
|
|
|
|
|
|
inline tl::expected<T, Error> to_expected() &&
|
|
|
|
{
|
|
|
|
return *static_cast<Storage*>(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Map>
|
|
|
|
requires is_template_v<Result, std::invoke_result_t<Map, T&&>>
|
|
|
|
auto and_then(Map &&map) &&
|
|
|
|
{
|
|
|
|
return std::move(*static_cast<Storage*>(this)).and_then(
|
|
|
|
[map = std::forward<Map>(map)](T &&value) {
|
|
|
|
return std::move(map)(std::move(value)).to_expected();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
using Storage::and_then;
|
2022-05-08 00:03:22 +02:00
|
|
|
};
|
2022-04-24 15:27:09 +02:00
|
|
|
|
|
|
|
// NOTE This implementation requires C++ language extension: statement expressions
|
2022-05-08 00:03:22 +02:00
|
|
|
// It's supported by GCC and Clang, other compilers i don't know
|
|
|
|
//
|
|
|
|
// Inspired by SerenityOS TRY macro
|
|
|
|
#define Try(Value) \
|
|
|
|
({ \
|
|
|
|
auto try_value = (Value); \
|
|
|
|
if (not try_value.has_value()) [[unlikely]] \
|
|
|
|
return tl::unexpected(try_value.error()); \
|
|
|
|
std::move(try_value).value(); \
|
2022-04-24 15:27:09 +02:00
|
|
|
})
|
|
|
|
|
2022-04-27 14:37:21 +02:00
|
|
|
namespace unicode
|
|
|
|
{
|
|
|
|
inline namespace special_runes
|
|
|
|
{
|
2022-05-08 00:03:22 +02:00
|
|
|
[[maybe_unused]] constexpr u32 Rune_Error = 0xfffd;
|
|
|
|
[[maybe_unused]] constexpr u32 Rune_Self = 0x80;
|
|
|
|
[[maybe_unused]] constexpr u32 Max_Bytes = 4;
|
2022-04-27 14:37:21 +02:00
|
|
|
}
|
|
|
|
|
2022-05-02 19:24:29 +02:00
|
|
|
// is_digit returns true if `digit` is ASCII digit
|
2022-04-27 14:37:21 +02:00
|
|
|
bool is_digit(u32 digit);
|
2022-05-02 19:24:29 +02:00
|
|
|
|
|
|
|
// is_space return true if `space` is ASCII blank character
|
2022-04-27 14:37:21 +02:00
|
|
|
bool is_space(u32 space);
|
2022-05-02 19:24:29 +02:00
|
|
|
|
|
|
|
// is_letter returns true if `letter` is considered a letter by Unicode
|
2022-05-02 19:00:11 +02:00
|
|
|
bool is_letter(u32 letter);
|
2022-05-02 19:24:29 +02:00
|
|
|
|
|
|
|
// is_identifier returns true if `letter` is valid character for identifier.
|
|
|
|
//
|
|
|
|
// It's modifier by is_first_character flag to determine some character classes
|
|
|
|
// allowance like numbers, which are only allowed NOT at the front of the identifier
|
|
|
|
enum class First_Character : bool { Yes = true, No = false };
|
|
|
|
bool is_identifier(u32 letter, First_Character is_first_character);
|
2022-04-27 14:37:21 +02:00
|
|
|
}
|
|
|
|
|
2022-04-27 13:48:50 +02:00
|
|
|
namespace utf8
|
|
|
|
{
|
2022-04-27 14:37:21 +02:00
|
|
|
using namespace unicode::special_runes;
|
2022-04-27 13:48:50 +02:00
|
|
|
|
|
|
|
// Decodes rune and returns remaining string
|
2022-05-02 14:50:04 +02:00
|
|
|
auto decode(std::string_view s) -> std::pair<u32, std::string_view>;
|
|
|
|
auto length(std::string_view s) -> usize;
|
|
|
|
|
|
|
|
struct Print { u32 rune; };
|
2022-04-27 13:48:50 +02:00
|
|
|
}
|
|
|
|
|
2022-05-02 14:50:04 +02:00
|
|
|
std::ostream& operator<<(std::ostream& os, utf8::Print const& print);
|
|
|
|
|
2022-04-24 15:27:09 +02:00
|
|
|
struct Token
|
|
|
|
{
|
|
|
|
enum class Type
|
|
|
|
{
|
|
|
|
// like repeat or choose or chord
|
|
|
|
Symbol,
|
|
|
|
|
2022-05-02 19:42:57 +02:00
|
|
|
// like + - ++ < >
|
|
|
|
Operator,
|
|
|
|
|
2022-04-24 15:27:09 +02:00
|
|
|
// chord literal, like c125
|
|
|
|
Chord,
|
|
|
|
|
|
|
|
// numeric literal (floating point or integer)
|
|
|
|
Numeric,
|
|
|
|
|
|
|
|
// "|" separaters arguments from block body, and provides variable introduction syntax
|
2022-05-16 16:58:31 +02:00
|
|
|
Parameter_Separator,
|
2022-04-24 15:27:09 +02:00
|
|
|
|
2022-05-07 18:20:22 +02:00
|
|
|
// ";" separates expressions. Used to separate calls, like `foo 1 2; bar 3 4`
|
|
|
|
Expression_Separator,
|
|
|
|
|
2022-04-24 15:27:09 +02:00
|
|
|
// "[" and "]", delimit anonymous block of code (potentially a function)
|
|
|
|
Open_Block,
|
|
|
|
Close_Block,
|
|
|
|
|
|
|
|
// "(" and ")", used in arithmetic or as function invocation sarrounding (like in Haskell)
|
|
|
|
Open_Paren,
|
|
|
|
Close_Paren
|
|
|
|
};
|
|
|
|
|
2022-04-24 16:09:55 +02:00
|
|
|
Type type;
|
2022-04-24 15:27:09 +02:00
|
|
|
std::string_view source;
|
2022-04-27 14:58:02 +02:00
|
|
|
Location location;
|
2022-04-24 15:27:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
std::ostream& operator<<(std::ostream& os, Token const& tok);
|
2022-05-02 19:42:57 +02:00
|
|
|
std::ostream& operator<<(std::ostream& os, Token::Type type);
|
2022-04-24 15:27:09 +02:00
|
|
|
|
|
|
|
struct Lexer
|
|
|
|
{
|
|
|
|
// Source that is beeing lexed
|
|
|
|
std::string_view source;
|
|
|
|
|
2022-04-27 13:48:50 +02:00
|
|
|
// Used for rewinding
|
|
|
|
u32 last_rune_length = 0;
|
|
|
|
|
2022-04-27 14:37:21 +02:00
|
|
|
char const* token_start = nullptr;
|
|
|
|
usize token_length = 0;
|
2022-04-27 14:58:02 +02:00
|
|
|
Location token_location{};
|
2022-04-27 14:37:21 +02:00
|
|
|
|
2022-04-27 14:58:02 +02:00
|
|
|
Location prev_location{};
|
|
|
|
Location location{};
|
2022-04-24 15:27:09 +02:00
|
|
|
|
|
|
|
auto next_token() -> Result<Token>;
|
2022-04-27 13:48:50 +02:00
|
|
|
|
2022-05-02 21:23:01 +02:00
|
|
|
// Utility function for next_token()
|
|
|
|
void skip_whitespace_and_comments();
|
|
|
|
|
2022-04-27 13:48:50 +02:00
|
|
|
// Finds next rune in source
|
2022-04-27 14:37:21 +02:00
|
|
|
auto peek() const -> u32;
|
|
|
|
|
|
|
|
// Finds next rune in source and returns it, advancing the string
|
|
|
|
auto consume() -> u32;
|
|
|
|
|
2022-05-07 18:39:29 +02:00
|
|
|
// For test beeing
|
|
|
|
// callable, current rune is passed to test
|
|
|
|
// integral, current rune is tested for equality with test
|
|
|
|
// string, current rune is tested for beeing in it
|
|
|
|
// otherwise, current rune is tested for beeing in test
|
|
|
|
//
|
|
|
|
// When testing above yields truth, current rune is consumed.
|
|
|
|
// Returns if rune was consumed
|
|
|
|
auto consume_if(auto test) -> bool;
|
2022-04-27 13:48:50 +02:00
|
|
|
|
2022-05-07 18:39:29 +02:00
|
|
|
// Consume two runes with given tests otherwise backtrack
|
|
|
|
auto consume_if(auto first, auto second) -> bool;
|
2022-05-02 21:23:01 +02:00
|
|
|
|
2022-04-27 13:48:50 +02:00
|
|
|
// Goes back last rune
|
|
|
|
void rewind();
|
2022-04-27 14:37:21 +02:00
|
|
|
|
|
|
|
// Marks begin of token
|
|
|
|
void start();
|
|
|
|
|
|
|
|
// Marks end of token and returns it's matching source
|
|
|
|
std::string_view finish();
|
2022-04-24 15:27:09 +02:00
|
|
|
};
|
2022-05-07 20:52:09 +02:00
|
|
|
|
|
|
|
struct Ast
|
|
|
|
{
|
|
|
|
// Named constructors of AST structure
|
2022-05-09 19:42:02 +02:00
|
|
|
static Ast binary(Token, Ast lhs, Ast rhs);
|
2022-05-16 16:58:31 +02:00
|
|
|
static Ast block(Location location, Ast seq = sequence({}), std::vector<Ast> parameters = {});
|
2022-05-10 15:25:17 +02:00
|
|
|
static Ast call(std::vector<Ast> call);
|
2022-05-16 16:58:31 +02:00
|
|
|
static Ast literal(Token);
|
2022-05-10 16:03:30 +02:00
|
|
|
static Ast sequence(std::vector<Ast> call);
|
2022-05-16 16:58:31 +02:00
|
|
|
static Ast variable_declaration(Location loc, std::vector<Ast> lvalues, std::optional<Ast> rvalue);
|
2022-05-07 20:52:09 +02:00
|
|
|
|
|
|
|
enum class Type
|
|
|
|
{
|
2022-05-16 16:58:31 +02:00
|
|
|
Binary, // Binary operator application like `1` + `2`
|
|
|
|
Block, // Block expressions like `[42; hello]`
|
|
|
|
Call, // Function call application like `print 42`
|
|
|
|
Literal, // Compile time known constant like `c` or `1`
|
|
|
|
Sequence, // Several expressions sequences like `42`, `42; 32`
|
|
|
|
Variable_Declaration, // Declaration of a variable with optional value assigment like `var x = 10` or `var y`
|
2022-05-07 20:52:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
Type type;
|
2022-05-15 22:22:26 +02:00
|
|
|
Location location;
|
2022-05-07 20:52:09 +02:00
|
|
|
Token token;
|
2022-05-10 16:49:36 +02:00
|
|
|
std::vector<Ast> arguments{};
|
2022-05-07 20:52:09 +02:00
|
|
|
};
|
|
|
|
|
2022-05-09 19:42:02 +02:00
|
|
|
bool operator==(Ast const& lhs, Ast const& rhs);
|
2022-05-10 16:49:36 +02:00
|
|
|
std::ostream& operator<<(std::ostream& os, Ast::Type type);
|
2022-05-09 19:42:02 +02:00
|
|
|
std::ostream& operator<<(std::ostream& os, Ast const& tree);
|
2022-05-10 16:49:36 +02:00
|
|
|
void dump(Ast const& ast, unsigned indent = 0);
|
2022-05-09 19:42:02 +02:00
|
|
|
|
2022-05-07 20:52:09 +02:00
|
|
|
struct Parser
|
|
|
|
{
|
|
|
|
std::vector<Token> tokens;
|
|
|
|
unsigned token_id = 0;
|
|
|
|
|
|
|
|
// Parses whole source code producing Ast or Error
|
|
|
|
// using Parser structure internally
|
|
|
|
static Result<Ast> parse(std::string_view source, std::string_view filename);
|
|
|
|
|
2022-05-10 16:03:30 +02:00
|
|
|
Result<Ast> parse_sequence();
|
2022-05-07 20:52:09 +02:00
|
|
|
Result<Ast> parse_expression();
|
2022-05-10 15:25:17 +02:00
|
|
|
Result<Ast> parse_infix_expression();
|
|
|
|
Result<Ast> parse_atomic_expression();
|
2022-05-16 16:58:31 +02:00
|
|
|
Result<Ast> parse_variable_declaration();
|
|
|
|
|
|
|
|
Result<Ast> parse_identifier_with_trailing_separators();
|
2022-05-10 16:49:36 +02:00
|
|
|
Result<Ast> parse_identifier();
|
2022-05-07 20:52:09 +02:00
|
|
|
|
2022-05-10 15:25:17 +02:00
|
|
|
Result<Token> peek() const;
|
2022-05-10 14:20:23 +02:00
|
|
|
Result<Token::Type> peek_type() const;
|
2022-05-07 20:52:09 +02:00
|
|
|
Token consume();
|
|
|
|
|
|
|
|
// Tests if current token has given type
|
|
|
|
bool expect(Token::Type type) const;
|
2022-05-16 16:58:31 +02:00
|
|
|
bool expect(Token::Type type, std::string_view lexeme) const;
|
2022-05-07 20:52:09 +02:00
|
|
|
|
|
|
|
// Ensures that current token has one of types given.
|
|
|
|
// Otherwise returns error
|
2022-05-08 00:03:22 +02:00
|
|
|
Result<void> ensure(Token::Type type) const;
|
2022-05-07 20:52:09 +02:00
|
|
|
};
|
|
|
|
|
2022-05-15 23:03:09 +02:00
|
|
|
// Number type supporting integer and fractional constants
|
|
|
|
// Invariant: gcd(num, den) == 1, after any operation
|
|
|
|
struct Number
|
|
|
|
{
|
2022-05-16 00:06:27 +02:00
|
|
|
using value_type = i64;
|
|
|
|
value_type num = 0, den = 1;
|
2022-05-15 23:03:09 +02:00
|
|
|
|
2022-05-16 00:06:27 +02:00
|
|
|
constexpr Number() = default;
|
|
|
|
constexpr Number(Number const&) = default;
|
|
|
|
constexpr Number(Number &&) = default;
|
|
|
|
constexpr Number& operator=(Number const&) = default;
|
|
|
|
constexpr Number& operator=(Number &&) = default;
|
|
|
|
|
|
|
|
explicit Number(value_type v);
|
|
|
|
Number(value_type num, value_type den);
|
|
|
|
|
|
|
|
auto as_int() const -> value_type; // Returns self as int
|
|
|
|
auto simplify() const -> Number; // Returns self, but with gcd(num, den) == 1
|
|
|
|
void simplify_inplace(); // Update self, to have gcd(num, den) == 1
|
2022-05-15 23:03:09 +02:00
|
|
|
|
|
|
|
bool operator==(Number const&) const;
|
|
|
|
bool operator!=(Number const&) const;
|
|
|
|
std::strong_ordering operator<=>(Number const&) const;
|
|
|
|
|
|
|
|
Number operator+(Number const& rhs) const;
|
|
|
|
Number& operator+=(Number const& rhs);
|
|
|
|
Number operator-(Number const& rhs) const;
|
|
|
|
Number& operator-=(Number const& rhs);
|
|
|
|
Number operator*(Number const& rhs) const;
|
|
|
|
Number& operator*=(Number const& rhs);
|
|
|
|
Number operator/(Number const& rhs) const;
|
|
|
|
Number& operator/=(Number const& rhs);
|
2022-05-16 00:06:27 +02:00
|
|
|
|
|
|
|
static Result<Number> from(Token token);
|
2022-05-15 23:03:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
std::ostream& operator<<(std::ostream& os, Number const& num);
|
|
|
|
|
2022-05-17 02:35:51 +02:00
|
|
|
struct Value;
|
|
|
|
|
|
|
|
using Function = std::function<Result<Value>(std::vector<Value>)>;
|
|
|
|
|
|
|
|
template<typename T, typename ...XS>
|
|
|
|
constexpr auto is_one_of = (std::is_same_v<T, XS> || ...);
|
|
|
|
|
2022-05-16 02:18:53 +02:00
|
|
|
// TODO Add location
|
|
|
|
struct Value
|
|
|
|
{
|
|
|
|
static Result<Value> from(Token t);
|
|
|
|
static Value number(Number n);
|
|
|
|
static Value symbol(std::string s);
|
2022-05-17 02:35:51 +02:00
|
|
|
static Value lambda(Function f);
|
2022-05-16 02:18:53 +02:00
|
|
|
|
|
|
|
enum class Type
|
|
|
|
{
|
|
|
|
Nil,
|
|
|
|
Number,
|
|
|
|
Symbol,
|
2022-05-17 02:35:51 +02:00
|
|
|
Lambda,
|
2022-05-16 02:18:53 +02:00
|
|
|
};
|
|
|
|
|
2022-05-17 02:35:51 +02:00
|
|
|
Value() = default;
|
|
|
|
Value(Value const&) = default;
|
|
|
|
Value(Value &&) = default;
|
|
|
|
Value& operator=(Value const&) = default;
|
|
|
|
Value& operator=(Value &&) = default;
|
|
|
|
|
|
|
|
template<typename Callable>
|
|
|
|
requires (!std::is_same_v<std::remove_cvref_t<Callable>, Value>)
|
|
|
|
&& std::invocable<Callable, std::vector<Value>>
|
|
|
|
&& is_one_of<std::invoke_result_t<Callable, std::vector<Value>>, Value, Result<Value>>
|
|
|
|
inline Value(Callable &&callable)
|
|
|
|
: type{Type::Lambda}
|
|
|
|
{
|
|
|
|
if constexpr (std::is_same_v<Result<Value>, std::invoke_result_t<Callable, std::vector<Value>>>) {
|
|
|
|
f = std::move(callable);
|
|
|
|
} else {
|
|
|
|
f = [fun = std::move(callable)](std::vector<Value> args) -> Result<Value> {
|
|
|
|
return fun(std::move(args));
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-16 02:18:53 +02:00
|
|
|
Type type = Type::Nil;
|
|
|
|
Number n{};
|
2022-05-17 02:35:51 +02:00
|
|
|
Function f{};
|
2022-05-16 02:18:53 +02:00
|
|
|
|
|
|
|
// TODO Most strings should not be allocated by Value, but reference to string allocated previously
|
|
|
|
// Wrapper for std::string is needed that will allocate only when needed, middle ground between:
|
|
|
|
// std::string - always owning string type
|
|
|
|
// std::string_view - not-owning string type
|
|
|
|
std::string s{};
|
|
|
|
|
2022-05-17 02:35:51 +02:00
|
|
|
Result<Value> operator()(std::vector<Value> args);
|
|
|
|
|
2022-05-16 02:18:53 +02:00
|
|
|
bool operator==(Value const& other) const;
|
|
|
|
};
|
|
|
|
|
2022-05-17 02:35:51 +02:00
|
|
|
std::string_view type_name(Value::Type t);
|
|
|
|
|
2022-05-16 02:18:53 +02:00
|
|
|
std::ostream& operator<<(std::ostream& os, Value const& v);
|
|
|
|
|
2022-05-17 02:35:51 +02:00
|
|
|
struct Env
|
|
|
|
{
|
|
|
|
static std::vector<Env> *pool;
|
|
|
|
std::unordered_map<std::string, Value> variables;
|
|
|
|
usize parent_enviroment_id;
|
|
|
|
|
|
|
|
Env() = default;
|
|
|
|
Env(Env const&) = delete;
|
|
|
|
Env(Env &&) = default;
|
|
|
|
Env& operator=(Env const&) = delete;
|
|
|
|
Env& operator=(Env &&) = default;
|
|
|
|
|
|
|
|
static Env& global();
|
|
|
|
|
|
|
|
/// Defines new variable regardless of it's current existance
|
|
|
|
Env& force_define(std::string name, Value new_value);
|
|
|
|
Env& parent();
|
|
|
|
Value* find(std::string const& name);
|
|
|
|
|
|
|
|
usize operator++() const;
|
|
|
|
usize operator--();
|
|
|
|
|
|
|
|
static constexpr decltype(Env::parent_enviroment_id) Unused = -1;
|
|
|
|
};
|
2022-05-16 02:18:53 +02:00
|
|
|
|
|
|
|
struct Interpreter
|
|
|
|
{
|
|
|
|
std::ostream &out;
|
2022-05-16 15:51:38 +02:00
|
|
|
std::unordered_map<std::string, Function> operators;
|
2022-05-17 02:35:51 +02:00
|
|
|
std::vector<Env> env_pool;
|
|
|
|
usize current_env = 0;
|
2022-05-16 02:18:53 +02:00
|
|
|
|
|
|
|
Interpreter();
|
2022-05-17 02:35:51 +02:00
|
|
|
~Interpreter();
|
2022-05-16 02:18:53 +02:00
|
|
|
Interpreter(std::ostream& out);
|
|
|
|
Interpreter(Interpreter const&) = delete;
|
|
|
|
Interpreter(Interpreter &&) = default;
|
|
|
|
|
2022-05-17 02:35:51 +02:00
|
|
|
Env& env();
|
|
|
|
Env const& env() const;
|
|
|
|
|
2022-05-16 02:18:53 +02:00
|
|
|
Result<Value> eval(Ast &&ast);
|
|
|
|
};
|
|
|
|
|
2022-05-07 20:52:09 +02:00
|
|
|
namespace errors
|
|
|
|
{
|
|
|
|
Error unrecognized_character(u32 invalid_character);
|
|
|
|
Error unrecognized_character(u32 invalid_character, Location location);
|
|
|
|
|
|
|
|
Error unexpected_token(Token::Type expected, Token const& unexpected);
|
2022-05-10 15:25:17 +02:00
|
|
|
Error unexpected_token(Token const& unexpected);
|
2022-05-07 20:52:09 +02:00
|
|
|
Error unexpected_end_of_source(Location location);
|
|
|
|
|
2022-05-16 00:06:27 +02:00
|
|
|
Error failed_numeric_parsing(Location location, std::errc errc, std::string_view source);
|
|
|
|
|
2022-05-16 02:18:53 +02:00
|
|
|
Error function_not_defined(Value const& v);
|
2022-05-16 15:51:38 +02:00
|
|
|
Error unresolved_operator(Token const& op);
|
2022-05-16 16:58:31 +02:00
|
|
|
Error expected_keyword(Token const& unexpected, std::string_view keyword);
|
2022-05-16 02:18:53 +02:00
|
|
|
|
2022-05-17 02:35:51 +02:00
|
|
|
Error not_callable(std::optional<Location> location, Value::Type value_type);
|
|
|
|
|
2022-05-07 20:52:09 +02:00
|
|
|
[[noreturn]]
|
|
|
|
void all_tokens_were_not_parsed(std::span<Token>);
|
|
|
|
}
|