Major documentation of source code improvement
This commit is contained in:
parent
563786312c
commit
dc1322e4ea
2
Doxyfile
2
Doxyfile
@ -952,7 +952,7 @@ RECURSIVE = YES
|
|||||||
# Note that relative paths are relative to the directory from which doxygen is
|
# Note that relative paths are relative to the directory from which doxygen is
|
||||||
# run.
|
# run.
|
||||||
|
|
||||||
EXCLUDE = tests
|
EXCLUDE = tests src/main.cc
|
||||||
|
|
||||||
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
||||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||||
|
26
src/main.cc
26
src/main.cc
@ -17,6 +17,7 @@ static bool enable_repl = false;
|
|||||||
|
|
||||||
#define Ignore(Call) do { auto const ignore_ ## __LINE__ = (Call); (void) ignore_ ## __LINE__; } while(0)
|
#define Ignore(Call) do { auto const ignore_ ## __LINE__ = (Call); (void) ignore_ ## __LINE__; } while(0)
|
||||||
|
|
||||||
|
/// Pop string from front of an array
|
||||||
static std::string_view pop(std::span<char const*> &span)
|
static std::string_view pop(std::span<char const*> &span)
|
||||||
{
|
{
|
||||||
auto element = span.front();
|
auto element = span.front();
|
||||||
@ -24,8 +25,8 @@ static std::string_view pop(std::span<char const*> &span)
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]]
|
/// Print usage and exit
|
||||||
void usage()
|
[[noreturn]] void usage()
|
||||||
{
|
{
|
||||||
std::cerr <<
|
std::cerr <<
|
||||||
"usage: musique <options> [filename]\n"
|
"usage: musique <options> [filename]\n"
|
||||||
@ -42,6 +43,7 @@ void usage()
|
|||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Trim spaces from left an right
|
||||||
static void trim(std::string_view &s)
|
static void trim(std::string_view &s)
|
||||||
{
|
{
|
||||||
// left trim
|
// left trim
|
||||||
@ -61,11 +63,13 @@ static void trim(std::string_view &s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs interpreter on given source code
|
||||||
struct Runner
|
struct Runner
|
||||||
{
|
{
|
||||||
midi::ALSA alsa;
|
midi::ALSA alsa;
|
||||||
Interpreter interpreter;
|
Interpreter interpreter;
|
||||||
|
|
||||||
|
/// Setup interpreter and midi connection with given port
|
||||||
Runner(std::string port)
|
Runner(std::string port)
|
||||||
: alsa("musique")
|
: alsa("musique")
|
||||||
{
|
{
|
||||||
@ -84,6 +88,7 @@ struct Runner
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Run given source
|
||||||
Result<void> run(std::string_view source, std::string_view filename)
|
Result<void> run(std::string_view source, std::string_view filename)
|
||||||
{
|
{
|
||||||
auto ast = Try(Parser::parse(source, filename));
|
auto ast = Try(Parser::parse(source, filename));
|
||||||
@ -99,17 +104,20 @@ struct Runner
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// We make sure that through life of interpreter source code is allways allocated
|
/// All source code through life of the program should stay allocated, since
|
||||||
|
/// some of the strings are only views into source
|
||||||
std::vector<std::string> eternal_sources;
|
std::vector<std::string> eternal_sources;
|
||||||
|
|
||||||
struct Run
|
/// Fancy main that supports Result forwarding on error (Try macro)
|
||||||
{
|
|
||||||
bool is_file = true;
|
|
||||||
std::string_view argument;
|
|
||||||
};
|
|
||||||
|
|
||||||
static Result<void> Main(std::span<char const*> args)
|
static Result<void> Main(std::span<char const*> args)
|
||||||
{
|
{
|
||||||
|
/// Describes all arguments that will be run
|
||||||
|
struct Run
|
||||||
|
{
|
||||||
|
bool is_file = true;
|
||||||
|
std::string_view argument;
|
||||||
|
};
|
||||||
|
|
||||||
std::vector<Run> runnables;
|
std::vector<Run> runnables;
|
||||||
|
|
||||||
while (not args.empty()) {
|
while (not args.empty()) {
|
||||||
|
333
src/musique.hh
333
src/musique.hh
@ -39,8 +39,10 @@ using i64 = std::int64_t;
|
|||||||
using usize = std::size_t;
|
using usize = std::size_t;
|
||||||
using isize = std::ptrdiff_t;
|
using isize = std::ptrdiff_t;
|
||||||
|
|
||||||
// Error handling mechanism inspired by Andrew Kelly approach, that was implemented
|
/// Error handling related functions and definitions
|
||||||
// as first class feature in Zig programming language.
|
///
|
||||||
|
/// Error handling mechanism inspired by Andrew Kelly approach, that was implemented
|
||||||
|
/// as first class feature in Zig programming language.
|
||||||
namespace errors
|
namespace errors
|
||||||
{
|
{
|
||||||
enum Type
|
enum Type
|
||||||
@ -100,12 +102,13 @@ struct Location
|
|||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, Location const& location);
|
std::ostream& operator<<(std::ostream& os, Location const& location);
|
||||||
|
|
||||||
|
/// Guards that program exits if condition does not hold
|
||||||
void assert(bool condition, std::string message, Location loc = Location::caller());
|
void assert(bool condition, std::string message, Location loc = Location::caller());
|
||||||
|
|
||||||
// Marks part of code that was not implemented yet
|
/// Marks part of code that was not implemented yet
|
||||||
[[noreturn]] void unimplemented(std::string_view message = {}, Location loc = Location::caller());
|
[[noreturn]] void unimplemented(std::string_view message = {}, Location loc = Location::caller());
|
||||||
|
|
||||||
// Marks location that should not be reached
|
/// Marks location that should not be reached
|
||||||
[[noreturn]] void unreachable(Location loc = Location::caller());
|
[[noreturn]] void unreachable(Location loc = Location::caller());
|
||||||
|
|
||||||
struct Error
|
struct Error
|
||||||
@ -189,10 +192,11 @@ struct [[nodiscard("This value may contain critical error, so it should NOT be i
|
|||||||
using Storage::and_then;
|
using Storage::and_then;
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE This implementation requires C++ language extension: statement expressions
|
/// Shorthand for forwarding error values with Result type family.
|
||||||
// It's supported by GCC and Clang, other compilers i don't know
|
///
|
||||||
//
|
/// This implementation requires C++ language extension: statement expressions
|
||||||
// Inspired by SerenityOS TRY macro
|
/// It's supported by GCC and Clang, other compilers i don't know.
|
||||||
|
/// Inspired by SerenityOS TRY macro
|
||||||
#define Try(Value) \
|
#define Try(Value) \
|
||||||
({ \
|
({ \
|
||||||
auto try_value = (Value); \
|
auto try_value = (Value); \
|
||||||
@ -218,6 +222,7 @@ struct Explicit_Bool
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// All unicode related operations
|
||||||
namespace unicode
|
namespace unicode
|
||||||
{
|
{
|
||||||
inline namespace special_runes
|
inline namespace special_runes
|
||||||
@ -227,29 +232,32 @@ namespace unicode
|
|||||||
[[maybe_unused]] constexpr u32 Max_Bytes = 4;
|
[[maybe_unused]] constexpr u32 Max_Bytes = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// is_digit returns true if `digit` is ASCII digit
|
/// is_digit returns true if `digit` is ASCII digit
|
||||||
bool is_digit(u32 digit);
|
bool is_digit(u32 digit);
|
||||||
|
|
||||||
// is_space return true if `space` is ASCII blank character
|
/// is_space return true if `space` is ASCII blank character
|
||||||
bool is_space(u32 space);
|
bool is_space(u32 space);
|
||||||
|
|
||||||
// is_letter returns true if `letter` is considered a letter by Unicode
|
/// is_letter returns true if `letter` is considered a letter by Unicode
|
||||||
bool is_letter(u32 letter);
|
bool is_letter(u32 letter);
|
||||||
|
|
||||||
// is_identifier returns true if `letter` is valid character for identifier.
|
/// is_identifier returns true if `letter` is valid character for identifier.
|
||||||
//
|
///
|
||||||
// It's modifier by is_first_character flag to determine some character classes
|
/// 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
|
/// allowance like numbers, which are only allowed NOT at the front of the identifier
|
||||||
enum class First_Character : bool { Yes = true, No = false };
|
enum class First_Character : bool { Yes = true, No = false };
|
||||||
bool is_identifier(u32 letter, First_Character is_first_character);
|
bool is_identifier(u32 letter, First_Character is_first_character);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// utf8 encoding and decoding
|
||||||
namespace utf8
|
namespace utf8
|
||||||
{
|
{
|
||||||
using namespace unicode::special_runes;
|
using namespace unicode::special_runes;
|
||||||
|
|
||||||
// Decodes rune and returns remaining string
|
/// Decodes rune and returns remaining string
|
||||||
auto decode(std::string_view s) -> std::pair<u32, std::string_view>;
|
auto decode(std::string_view s) -> std::pair<u32, std::string_view>;
|
||||||
|
|
||||||
|
/// Returns length of the first rune in the provided string
|
||||||
auto length(std::string_view s) -> usize;
|
auto length(std::string_view s) -> usize;
|
||||||
|
|
||||||
struct Print { u32 rune; };
|
struct Print { u32 rune; };
|
||||||
@ -257,167 +265,231 @@ namespace utf8
|
|||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, utf8::Print const& print);
|
std::ostream& operator<<(std::ostream& os, utf8::Print const& print);
|
||||||
|
|
||||||
|
/// Lexical token representation for Musique language
|
||||||
struct Token
|
struct Token
|
||||||
{
|
{
|
||||||
|
/// Type of Token
|
||||||
enum class Type
|
enum class Type
|
||||||
{
|
{
|
||||||
// like repeat or choose or chord
|
Symbol, ///< like repeat or choose or chord
|
||||||
Symbol,
|
Keyword, ///< like true, false, nil
|
||||||
|
Operator, ///< like "+", "-", "++", "<"
|
||||||
// like true, false, nil
|
Chord, ///< chord or single note literal, like "c125"
|
||||||
Keyword,
|
Numeric, ///< numeric literal (floating point or integer)
|
||||||
|
Parameter_Separator, ///< "|" separaters arguments from block body
|
||||||
// like + - ++ < >
|
Expression_Separator, ///< ";" separates expressions. Used mainly to separate calls, like `foo 1 2; bar 3 4`
|
||||||
Operator,
|
Open_Block, ///< "[" delimits anonymous block of code (potentially a function)
|
||||||
|
Close_Block, ///< "]" delimits anonymous block of code (potentially a function)
|
||||||
// chord literal, like c125
|
Open_Paren, ///< "(" used in arithmetic or as function invocation sarrounding
|
||||||
Chord,
|
Close_Paren ///< ")" used in arithmetic or as function invocation sarrounding
|
||||||
|
|
||||||
// numeric literal (floating point or integer)
|
|
||||||
Numeric,
|
|
||||||
|
|
||||||
// "|" separaters arguments from block body, and provides variable introduction syntax
|
|
||||||
Parameter_Separator,
|
|
||||||
|
|
||||||
// ";" separates expressions. Used to separate calls, like `foo 1 2; bar 3 4`
|
|
||||||
Expression_Separator,
|
|
||||||
|
|
||||||
// "[" 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
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Type of token
|
||||||
Type type;
|
Type type;
|
||||||
|
|
||||||
|
/// Matched source code to the token type
|
||||||
std::string_view source;
|
std::string_view source;
|
||||||
|
|
||||||
|
/// Location of encountered token
|
||||||
Location location;
|
Location location;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Token debug printing
|
||||||
std::ostream& operator<<(std::ostream& os, Token const& tok);
|
std::ostream& operator<<(std::ostream& os, Token const& tok);
|
||||||
|
|
||||||
|
/// Token type debug printing
|
||||||
std::ostream& operator<<(std::ostream& os, Token::Type type);
|
std::ostream& operator<<(std::ostream& os, Token::Type type);
|
||||||
|
|
||||||
|
/// Lexer takes source code and turns it into list of tokens
|
||||||
|
///
|
||||||
|
/// It allows for creating sequence of tokens by using next_token() method.
|
||||||
|
/// On each call to next_token() when source is non empty token is lexed and
|
||||||
|
/// source is beeing advanced by removing matched token from string.
|
||||||
struct Lexer
|
struct Lexer
|
||||||
{
|
{
|
||||||
// Source that is beeing lexed
|
/// Source that is beeing lexed
|
||||||
std::string_view source;
|
std::string_view source;
|
||||||
|
|
||||||
// Used for rewinding
|
/// Location in source of the last rune
|
||||||
|
///
|
||||||
|
/// Used only for rewinding
|
||||||
u32 last_rune_length = 0;
|
u32 last_rune_length = 0;
|
||||||
|
|
||||||
|
/// Start of the token that is currently beeing matched
|
||||||
char const* token_start = nullptr;
|
char const* token_start = nullptr;
|
||||||
|
|
||||||
|
/// Bytes matched so far
|
||||||
usize token_length = 0;
|
usize token_length = 0;
|
||||||
|
|
||||||
|
/// Location of the start of a token that is currently beeing matched
|
||||||
Location token_location{};
|
Location token_location{};
|
||||||
|
|
||||||
Location prev_location{};
|
/// Current location of Lexer in source
|
||||||
Location location{};
|
Location location{};
|
||||||
|
|
||||||
|
/// Previous location of Lexer in source
|
||||||
|
///
|
||||||
|
/// Used only for rewinding
|
||||||
|
Location prev_location{};
|
||||||
|
|
||||||
|
/// Try to tokenize next token
|
||||||
auto next_token() -> Result<Token>;
|
auto next_token() -> Result<Token>;
|
||||||
|
|
||||||
// Utility function for next_token()
|
/// Skip whitespace and comments from the beggining of the source
|
||||||
|
///
|
||||||
|
/// Utility function for next_token()
|
||||||
void skip_whitespace_and_comments();
|
void skip_whitespace_and_comments();
|
||||||
|
|
||||||
// Finds next rune in source
|
/// Finds next rune in source
|
||||||
auto peek() const -> u32;
|
auto peek() const -> u32;
|
||||||
|
|
||||||
// Finds next rune in source and returns it, advancing the string
|
/// Finds next rune in source and returns it, advancing the string
|
||||||
auto consume() -> u32;
|
auto consume() -> u32;
|
||||||
|
|
||||||
// For test beeing
|
/// For test beeing
|
||||||
// callable, current rune is passed to test
|
/// callable, current rune is passed to test
|
||||||
// integral, current rune is tested for equality with test
|
/// integral, current rune is tested for equality with test
|
||||||
// string, current rune is tested for beeing in it
|
/// string, current rune is tested for beeing in it
|
||||||
// otherwise, current rune is tested for beeing in test
|
/// otherwise, current rune is tested for beeing in test
|
||||||
//
|
///
|
||||||
// When testing above yields truth, current rune is consumed.
|
/// When testing above yields truth, current rune is consumed.
|
||||||
// Returns if rune was consumed
|
/// Returns if rune was consumed
|
||||||
auto consume_if(auto test) -> bool;
|
auto consume_if(auto test) -> bool;
|
||||||
|
|
||||||
// Consume two runes with given tests otherwise backtrack
|
/// Consume two runes with given tests otherwise backtrack
|
||||||
auto consume_if(auto first, auto second) -> bool;
|
auto consume_if(auto first, auto second) -> bool;
|
||||||
|
|
||||||
// Goes back last rune
|
/// Goes back last rune
|
||||||
void rewind();
|
void rewind();
|
||||||
|
|
||||||
// Marks begin of token
|
/// Marks begin of token
|
||||||
void start();
|
void start();
|
||||||
|
|
||||||
// Marks end of token and returns it's matching source
|
/// Marks end of token and returns it's matching source
|
||||||
std::string_view finish();
|
std::string_view finish();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Representation of a node in program tree
|
||||||
struct Ast
|
struct Ast
|
||||||
{
|
{
|
||||||
// Named constructors of AST structure
|
/// Constructs binary operator
|
||||||
static Ast binary(Token, Ast lhs, Ast rhs);
|
static Ast binary(Token, Ast lhs, Ast rhs);
|
||||||
|
|
||||||
|
/// Constructs block
|
||||||
static Ast block(Location location, Ast seq = sequence({}));
|
static Ast block(Location location, Ast seq = sequence({}));
|
||||||
|
|
||||||
|
/// Constructs call expression
|
||||||
static Ast call(std::vector<Ast> call);
|
static Ast call(std::vector<Ast> call);
|
||||||
|
|
||||||
|
/// Constructs block with parameters
|
||||||
static Ast lambda(Location location, Ast seq = sequence({}), std::vector<Ast> parameters = {});
|
static Ast lambda(Location location, Ast seq = sequence({}), std::vector<Ast> parameters = {});
|
||||||
|
|
||||||
|
/// Constructs constants, literals and variable identifiers
|
||||||
static Ast literal(Token);
|
static Ast literal(Token);
|
||||||
|
|
||||||
|
/// Constructs sequence of operations
|
||||||
static Ast sequence(std::vector<Ast> call);
|
static Ast sequence(std::vector<Ast> call);
|
||||||
|
|
||||||
|
/// Constructs variable declaration
|
||||||
static Ast variable_declaration(Location loc, std::vector<Ast> lvalues, std::optional<Ast> rvalue);
|
static Ast variable_declaration(Location loc, std::vector<Ast> lvalues, std::optional<Ast> rvalue);
|
||||||
|
|
||||||
|
/// Available ASt types
|
||||||
enum class Type
|
enum class Type
|
||||||
{
|
{
|
||||||
Binary, // Binary operator application like `1` + `2`
|
Binary, ///< Binary operator application like `1` + `2`
|
||||||
Block, // Block expressions like `[42; hello]`
|
Block, ///< Block expressions like `[42; hello]`
|
||||||
Lambda, // Block expression beeing functions like `[i|i+1]`
|
Lambda, ///< Block expression beeing functions like `[i|i+1]`
|
||||||
Call, // Function call application like `print 42`
|
Call, ///< Function call application like `print 42`
|
||||||
Literal, // Compile time known constant like `c` or `1`
|
Literal, ///< Compile time known constant like `c` or `1`
|
||||||
Sequence, // Several expressions sequences like `42`, `42; 32`
|
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`
|
Variable_Declaration, ///< Declaration of a variable with optional value assigment like `var x = 10` or `var y`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Type of AST node
|
||||||
Type type;
|
Type type;
|
||||||
|
|
||||||
|
/// Location that introduced this node
|
||||||
Location location;
|
Location location;
|
||||||
|
|
||||||
|
/// Associated token
|
||||||
Token token;
|
Token token;
|
||||||
|
|
||||||
|
/// Child nodes
|
||||||
std::vector<Ast> arguments{};
|
std::vector<Ast> arguments{};
|
||||||
};
|
};
|
||||||
|
|
||||||
bool operator==(Ast const& lhs, Ast const& rhs);
|
bool operator==(Ast const& lhs, Ast const& rhs);
|
||||||
std::ostream& operator<<(std::ostream& os, Ast::Type type);
|
std::ostream& operator<<(std::ostream& os, Ast::Type type);
|
||||||
std::ostream& operator<<(std::ostream& os, Ast const& tree);
|
std::ostream& operator<<(std::ostream& os, Ast const& tree);
|
||||||
|
|
||||||
|
/// Pretty print program tree for debugging purposes
|
||||||
void dump(Ast const& ast, unsigned indent = 0);
|
void dump(Ast const& ast, unsigned indent = 0);
|
||||||
|
|
||||||
|
/// Source code to program tree converter
|
||||||
|
///
|
||||||
|
/// Intended to be used by library user only by Parser::parse() static function.
|
||||||
struct Parser
|
struct Parser
|
||||||
{
|
{
|
||||||
|
/// List of tokens yielded from source
|
||||||
std::vector<Token> tokens;
|
std::vector<Token> tokens;
|
||||||
|
|
||||||
|
/// Current token id (offset in tokens array)
|
||||||
unsigned token_id = 0;
|
unsigned token_id = 0;
|
||||||
|
|
||||||
// Parses whole source code producing Ast or Error
|
/// Parses whole source code producing Ast or Error
|
||||||
// using Parser structure internally
|
/// using Parser structure internally
|
||||||
static Result<Ast> parse(std::string_view source, std::string_view filename);
|
static Result<Ast> parse(std::string_view source, std::string_view filename);
|
||||||
|
|
||||||
|
/// Parse sequence, collection of expressions
|
||||||
Result<Ast> parse_sequence();
|
Result<Ast> parse_sequence();
|
||||||
|
|
||||||
|
/// Parse either infix expression or variable declaration
|
||||||
Result<Ast> parse_expression();
|
Result<Ast> parse_expression();
|
||||||
|
|
||||||
|
/// Parse infix expression
|
||||||
Result<Ast> parse_infix_expression();
|
Result<Ast> parse_infix_expression();
|
||||||
|
|
||||||
|
/// Parse function call, literal etc
|
||||||
Result<Ast> parse_atomic_expression();
|
Result<Ast> parse_atomic_expression();
|
||||||
|
|
||||||
|
/// Parse variable declaration
|
||||||
Result<Ast> parse_variable_declaration();
|
Result<Ast> parse_variable_declaration();
|
||||||
|
|
||||||
|
/// Utility function for identifier parsing
|
||||||
Result<Ast> parse_identifier_with_trailing_separators();
|
Result<Ast> parse_identifier_with_trailing_separators();
|
||||||
|
|
||||||
|
/// Utility function for identifier parsing
|
||||||
Result<Ast> parse_identifier();
|
Result<Ast> parse_identifier();
|
||||||
|
|
||||||
|
/// Peek current token
|
||||||
Result<Token> peek() const;
|
Result<Token> peek() const;
|
||||||
|
|
||||||
|
/// Peek type of the current token
|
||||||
Result<Token::Type> peek_type() const;
|
Result<Token::Type> peek_type() const;
|
||||||
|
|
||||||
|
/// Consume current token
|
||||||
Token consume();
|
Token consume();
|
||||||
|
|
||||||
// Tests if current token has given type
|
/// Tests if current token has given type
|
||||||
bool expect(Token::Type type) const;
|
bool expect(Token::Type type) const;
|
||||||
|
|
||||||
|
/// Tests if current token has given type and source
|
||||||
bool expect(Token::Type type, std::string_view lexeme) const;
|
bool expect(Token::Type type, std::string_view lexeme) const;
|
||||||
|
|
||||||
// Ensures that current token has one of types given.
|
/// Ensures that current token has one of types given, otherwise returns error
|
||||||
// Otherwise returns error
|
|
||||||
Result<void> ensure(Token::Type type) const;
|
Result<void> ensure(Token::Type type) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Number type supporting integer and fractional constants
|
/// Number type supporting integer and fractional constants
|
||||||
// Invariant: gcd(num, den) == 1, after any operation
|
///
|
||||||
|
/// \invariant gcd(num, den) == 1, after any operation
|
||||||
struct Number
|
struct Number
|
||||||
{
|
{
|
||||||
|
/// Type that represents numerator and denominator values
|
||||||
using value_type = i64;
|
using value_type = i64;
|
||||||
value_type num = 0, den = 1;
|
|
||||||
|
value_type num = 0; ///< Numerator of a fraction beeing represented
|
||||||
|
value_type den = 1; ///< Denominator of a fraction beeing represented
|
||||||
|
|
||||||
constexpr Number() = default;
|
constexpr Number() = default;
|
||||||
constexpr Number(Number const&) = default;
|
constexpr Number(Number const&) = default;
|
||||||
@ -425,12 +497,12 @@ struct Number
|
|||||||
constexpr Number& operator=(Number const&) = default;
|
constexpr Number& operator=(Number const&) = default;
|
||||||
constexpr Number& operator=(Number &&) = default;
|
constexpr Number& operator=(Number &&) = default;
|
||||||
|
|
||||||
explicit Number(value_type v);
|
explicit Number(value_type v); ///< Creates Number as fraction v / 1
|
||||||
Number(value_type num, value_type den);
|
Number(value_type num, value_type den); ///< Creates Number as fraction num / den
|
||||||
|
|
||||||
auto as_int() const -> value_type; // Returns self as int
|
auto as_int() const -> value_type; ///< Returns self as int
|
||||||
auto simplify() const -> Number; // Returns self, but with gcd(num, den) == 1
|
auto simplify() const -> Number; ///< Returns self, but with gcd(num, den) == 1
|
||||||
void simplify_inplace(); // Update self, to have gcd(num, den) == 1
|
void simplify_inplace(); ///< Update self, to have gcd(num, den) == 1
|
||||||
|
|
||||||
bool operator==(Number const&) const;
|
bool operator==(Number const&) const;
|
||||||
bool operator!=(Number const&) const;
|
bool operator!=(Number const&) const;
|
||||||
@ -445,6 +517,7 @@ struct Number
|
|||||||
Number operator/(Number const& rhs) const;
|
Number operator/(Number const& rhs) const;
|
||||||
Number& operator/=(Number const& rhs);
|
Number& operator/=(Number const& rhs);
|
||||||
|
|
||||||
|
/// Parses source contained by token into a Number instance
|
||||||
static Result<Number> from(Token token);
|
static Result<Number> from(Token token);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -459,16 +532,29 @@ using Intrinsic = Result<Value>(*)(Interpreter &i, std::vector<Value>);
|
|||||||
/// Lazy Array / Continuation / Closure type thingy
|
/// Lazy Array / Continuation / Closure type thingy
|
||||||
struct Block
|
struct Block
|
||||||
{
|
{
|
||||||
|
/// Location of definition / creation
|
||||||
Location location;
|
Location location;
|
||||||
|
|
||||||
|
/// Names of expected parameters
|
||||||
std::vector<std::string> parameters;
|
std::vector<std::string> parameters;
|
||||||
|
|
||||||
|
/// Body that will be executed
|
||||||
Ast body;
|
Ast body;
|
||||||
|
|
||||||
|
/// Context from which block was created. Used for closures
|
||||||
std::shared_ptr<Env> context;
|
std::shared_ptr<Env> context;
|
||||||
|
|
||||||
|
/// Calling block
|
||||||
Result<Value> operator()(Interpreter &i, std::vector<Value> params);
|
Result<Value> operator()(Interpreter &i, std::vector<Value> params);
|
||||||
|
|
||||||
|
/// Indexing block
|
||||||
Result<Value> index(Interpreter &i, unsigned position);
|
Result<Value> index(Interpreter &i, unsigned position);
|
||||||
|
|
||||||
|
/// Count of elements in block
|
||||||
usize size() const;
|
usize size() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Representation of musical note
|
||||||
struct Note
|
struct Note
|
||||||
{
|
{
|
||||||
/// Base of a note, like `c` (=0), `c#` (=1) `d` (=2)
|
/// Base of a note, like `c` (=0), `c#` (=1) `d` (=2)
|
||||||
@ -491,15 +577,18 @@ struct Note
|
|||||||
|
|
||||||
bool operator==(Note const&) const;
|
bool operator==(Note const&) const;
|
||||||
|
|
||||||
|
/// Simplify note by adding base to octave if octave is present
|
||||||
void simplify_inplace();
|
void simplify_inplace();
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, Note note);
|
std::ostream& operator<<(std::ostream& os, Note note);
|
||||||
|
|
||||||
|
/// Represantation of simultaneously played notes, aka musical chord
|
||||||
struct Chord
|
struct Chord
|
||||||
{
|
{
|
||||||
std::vector<Note> notes;
|
std::vector<Note> notes; ///< Notes composing a chord
|
||||||
|
|
||||||
|
/// Parse chord literal from provided source
|
||||||
static Chord from(std::string_view source);
|
static Chord from(std::string_view source);
|
||||||
|
|
||||||
bool operator==(Chord const&) const = default;
|
bool operator==(Chord const&) const = default;
|
||||||
@ -513,7 +602,10 @@ struct Array
|
|||||||
/// Elements that are stored in array
|
/// Elements that are stored in array
|
||||||
std::vector<Value> elements;
|
std::vector<Value> elements;
|
||||||
|
|
||||||
|
/// Index element of an array
|
||||||
Result<Value> index(Interpreter &i, unsigned position);
|
Result<Value> index(Interpreter &i, unsigned position);
|
||||||
|
|
||||||
|
/// Count of elements
|
||||||
usize size() const;
|
usize size() const;
|
||||||
|
|
||||||
bool operator==(Array const&) const = default;
|
bool operator==(Array const&) const = default;
|
||||||
@ -521,33 +613,37 @@ struct Array
|
|||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, Array const& v);
|
std::ostream& operator<<(std::ostream& os, Array const& v);
|
||||||
|
|
||||||
// TODO Add location
|
/// Representation of any value in language
|
||||||
struct Value
|
struct Value
|
||||||
{
|
{
|
||||||
|
/// Creates value from literal contained in Token
|
||||||
static Result<Value> from(Token t);
|
static Result<Value> from(Token t);
|
||||||
|
|
||||||
|
/// Create value holding provided boolean
|
||||||
|
///
|
||||||
|
/// Using Explicit_Bool to prevent from implicit casts
|
||||||
static Value from(Explicit_Bool b);
|
static Value from(Explicit_Bool b);
|
||||||
static Value from(Number n);
|
|
||||||
|
|
||||||
// Symbol creating functions
|
|
||||||
static Value from(std::string s);
|
|
||||||
static Value from(std::string_view s);
|
|
||||||
static Value from(char const* s);
|
|
||||||
|
|
||||||
static Value from(Block &&l);
|
static Value from(Number n); ///< Create value of type number holding provided number
|
||||||
static Value from(Array &&array);
|
static Value from(std::string s); ///< Create value of type symbol holding provided symbol
|
||||||
static Value from(Note n);
|
static Value from(std::string_view s); ///< Create value of type symbol holding provided symbol
|
||||||
static Value from(Chord chord);
|
static Value from(char const* s); ///< Create value of type symbol holding provided symbol
|
||||||
|
static Value from(Block &&l); ///< Create value of type block holding provided block
|
||||||
|
static Value from(Array &&array); ///< Create value of type array holding provided array
|
||||||
|
static Value from(Note n); ///< Create value of type music holding provided note
|
||||||
|
static Value from(Chord chord); ///< Create value of type music holding provided chord
|
||||||
|
|
||||||
enum class Type
|
enum class Type
|
||||||
{
|
{
|
||||||
Nil,
|
Nil, ///< Unit type, used for denoting emptiness and result of some side effect only functions
|
||||||
Bool,
|
Bool, ///< Boolean type, used for logic computations
|
||||||
Number,
|
Number, ///< Number type, representing only rational numbers
|
||||||
Symbol,
|
Symbol, ///< Symbol type, used to represent identifiers
|
||||||
Intrinsic,
|
Intrinsic, ///< Intrinsic functions that are implemented in C++
|
||||||
Block,
|
Block, ///< Block type, containing block value (lazy array/closure/lambda like)
|
||||||
Array,
|
Array, ///< Array type, eager array
|
||||||
Music,
|
Music, ///< Music type,
|
||||||
};
|
};
|
||||||
|
|
||||||
Value() = default;
|
Value() = default;
|
||||||
@ -556,6 +652,7 @@ struct Value
|
|||||||
Value& operator=(Value const&) = default;
|
Value& operator=(Value const&) = default;
|
||||||
Value& operator=(Value &&) = default;
|
Value& operator=(Value &&) = default;
|
||||||
|
|
||||||
|
/// Contructs Intrinsic, used to simplify definition of intrinsics
|
||||||
inline Value(Intrinsic intr) : type{Type::Intrinsic}, intr(intr)
|
inline Value(Intrinsic intr) : type{Type::Intrinsic}, intr(intr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -574,26 +671,42 @@ struct Value
|
|||||||
// std::string_view - not-owning string type
|
// std::string_view - not-owning string type
|
||||||
std::string s{};
|
std::string s{};
|
||||||
|
|
||||||
|
/// Returns truth judgment for current type, used primarly for if function
|
||||||
bool truthy() const;
|
bool truthy() const;
|
||||||
|
|
||||||
|
/// Returns false judgment for current type, used primarly for if function
|
||||||
bool falsy() const;
|
bool falsy() const;
|
||||||
|
|
||||||
|
/// Calls contained value if it can be called
|
||||||
Result<Value> operator()(Interpreter &i, std::vector<Value> args);
|
Result<Value> operator()(Interpreter &i, std::vector<Value> args);
|
||||||
|
|
||||||
|
/// Index contained value if it can be called
|
||||||
Result<Value> index(Interpreter &i, unsigned position);
|
Result<Value> index(Interpreter &i, unsigned position);
|
||||||
|
|
||||||
|
/// Return elements count of contained value if it can be measured
|
||||||
usize size() const;
|
usize size() const;
|
||||||
|
|
||||||
bool operator==(Value const& other) const;
|
bool operator==(Value const& other) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Returns type name of Value type
|
||||||
std::string_view type_name(Value::Type t);
|
std::string_view type_name(Value::Type t);
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, Value const& v);
|
std::ostream& operator<<(std::ostream& os, Value const& v);
|
||||||
|
|
||||||
|
/// Collection holding all variables in given scope.
|
||||||
struct Env : std::enable_shared_from_this<Env>
|
struct Env : std::enable_shared_from_this<Env>
|
||||||
{
|
{
|
||||||
// Constructor of Env class
|
/// Constructor of Env class
|
||||||
static std::shared_ptr<Env> make();
|
static std::shared_ptr<Env> make();
|
||||||
|
|
||||||
|
/// Global scope that is beeing set by Interpreter
|
||||||
static std::shared_ptr<Env> global;
|
static std::shared_ptr<Env> global;
|
||||||
|
|
||||||
|
/// Variables in current scope
|
||||||
std::unordered_map<std::string, Value> variables;
|
std::unordered_map<std::string, Value> variables;
|
||||||
|
|
||||||
|
/// Parent scope
|
||||||
std::shared_ptr<Env> parent;
|
std::shared_ptr<Env> parent;
|
||||||
|
|
||||||
Env(Env const&) = delete;
|
Env(Env const&) = delete;
|
||||||
@ -603,14 +716,18 @@ struct Env : std::enable_shared_from_this<Env>
|
|||||||
|
|
||||||
/// Defines new variable regardless of it's current existance
|
/// Defines new variable regardless of it's current existance
|
||||||
Env& force_define(std::string name, Value new_value);
|
Env& force_define(std::string name, Value new_value);
|
||||||
|
|
||||||
|
/// Finds variable in current or parent scopes
|
||||||
Value* find(std::string const& name);
|
Value* find(std::string const& name);
|
||||||
|
|
||||||
// Scope menagment
|
/// Create new scope with self as parent
|
||||||
std::shared_ptr<Env> enter();
|
std::shared_ptr<Env> enter();
|
||||||
|
|
||||||
|
/// Leave current scope returning parent
|
||||||
std::shared_ptr<Env> leave();
|
std::shared_ptr<Env> leave();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Ensure that all values of this class are behind shared_ptr
|
/// Ensure that all values of this class are behind shared_ptr
|
||||||
Env() = default;
|
Env() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -633,6 +750,7 @@ struct Context
|
|||||||
std::chrono::duration<float> length_to_duration(std::optional<Number> length) const;
|
std::chrono::duration<float> length_to_duration(std::optional<Number> length) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Given program tree evaluates it into Value
|
||||||
struct Interpreter
|
struct Interpreter
|
||||||
{
|
{
|
||||||
/// MIDI connection that is used to play music.
|
/// MIDI connection that is used to play music.
|
||||||
@ -654,10 +772,13 @@ struct Interpreter
|
|||||||
Interpreter(Interpreter const&) = delete;
|
Interpreter(Interpreter const&) = delete;
|
||||||
Interpreter(Interpreter &&) = default;
|
Interpreter(Interpreter &&) = default;
|
||||||
|
|
||||||
|
/// Try to evaluate given program tree
|
||||||
Result<Value> eval(Ast &&ast);
|
Result<Value> eval(Ast &&ast);
|
||||||
|
|
||||||
// Scope managment
|
// Enter scope by changing current environment
|
||||||
void enter_scope();
|
void enter_scope();
|
||||||
|
|
||||||
|
// Leave scope by changing current environment
|
||||||
void leave_scope();
|
void leave_scope();
|
||||||
|
|
||||||
/// Play note resolving any missing parameters with context via `midi_connection` member.
|
/// Play note resolving any missing parameters with context via `midi_connection` member.
|
||||||
|
Loading…
Reference in New Issue
Block a user