Conforming to clang's standard library on MacOS

This commit is contained in:
Robert Bendun 2022-10-14 14:40:01 +02:00
parent 78b1e7d703
commit b93a236e37
26 changed files with 118 additions and 103 deletions

View File

@ -13,7 +13,7 @@ include scripts/windows.mk
# http://www.music.mcgill.ca/~gary/rtmidi/#compiling # http://www.music.mcgill.ca/~gary/rtmidi/#compiling
bin/rtmidi.o: lib/rtmidi/RtMidi.cpp lib/rtmidi/RtMidi.h bin/rtmidi.o: lib/rtmidi/RtMidi.cpp lib/rtmidi/RtMidi.h
@echo "CXX $@" @echo "CXX $@"
@$(CXX) $< -c -O2 -o $@ $(CPPFLAGS) @$(CXX) $< -c -O2 -o $@ $(CPPFLAGS) -std=c++20
doc: Doxyfile musique/*.cc musique/*.hh doc: Doxyfile musique/*.cc musique/*.hh
doxygen doxygen

View File

@ -1,9 +1,9 @@
MAKEFLAGS="-j $(grep -c ^processor /proc/cpuinfo)" MAKEFLAGS="-j $(grep -c ^processor /proc/cpuinfo)"
CXXFLAGS:=$(CXXFLAGS) -std=c++20 -Wall -Wextra -Werror=switch -Werror=return-type -Werror=unused-result -Wno-maybe-uninitialized CXXFLAGS:=$(CXXFLAGS) -std=c++20 -Wall -Wextra -Werror=switch -Werror=return-type -Werror=unused-result
CPPFLAGS:=$(CPPFLAGS) -Ilib/expected/ -I. -Ilib/bestline/ -Ilib/rtmidi/ CPPFLAGS:=$(CPPFLAGS) -Ilib/expected/ -I. -Ilib/bestline/ -Ilib/rtmidi/
LDFLAGS=-flto LDFLAGS=-flto
LDLIBS= -lpthread -static-libgcc -static-libstdc++ LDLIBS= -lpthread
RELEASE_FLAGS=-O2 RELEASE_FLAGS=-O2
DEBUG_FLAGS=-O0 -ggdb -fsanitize=undefined -DDebug DEBUG_FLAGS=-O0 -ggdb -fsanitize=undefined -DDebug
@ -16,7 +16,7 @@ LDLIBS:=-lwinmm $(LDLIBS)
else else
CC=gcc CC=gcc
CXX=g++ CXX=g++
CPPFLAGS:=$(CPPFLAGS) -D __LINUX_ALSA__ CPPFLAGS:=$(CPPFLAGS) -D __MACOSX_CORE__
LDLIBS:=-lasound $(LDLIBS) LDLIBS:=-framework CoreMIDI -framework CoreAudio -framework CoreFoundation $(LDLIBS)
endif endif

View File

@ -5,13 +5,14 @@
#include <musique/try.hh> #include <musique/try.hh>
#include <musique/value/value.hh> #include <musique/value/value.hh>
#include <ranges> #include <ranges>
#include <compare>
/// Generic algorithms support /// Generic algorithms support
namespace algo namespace algo
{ {
/// Check if predicate is true for all successive pairs of elements /// Check if predicate is true for all successive pairs of elements
constexpr bool pairwise_all( constexpr bool pairwise_all(
std::ranges::forward_range auto &&range, auto &&range,
auto &&binary_predicate) auto &&binary_predicate)
{ {
auto it = std::begin(range); auto it = std::begin(range);
@ -34,6 +35,22 @@ namespace algo
} }
return init; return init;
} }
/// Equavilent of std::lexicographical_compare_three_way
///
/// It's missing from MacOS C++ standard library
constexpr auto lexicographical_compare(auto&& lhs_range, auto&& rhs_range)
{
auto lhs = lhs_range.begin();
auto rhs = rhs_range.begin();
decltype(*lhs <=> *rhs) result = std::partial_ordering::equivalent;
for (; lhs != lhs_range.end() && rhs != rhs_range.end(); ++lhs, ++rhs) {
if ((result = *lhs <=> *rhs) != 0) {
return result;
}
}
return result;
}
} }
/// Flattens one layer: `[[[1], 2], 3]` becomes `[[1], 2, 3]` /// Flattens one layer: `[[[1], 2], 3]` becomes `[[1], 2, 3]`

View File

@ -54,4 +54,9 @@ constexpr std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
template<typename> template<typename>
static constexpr bool always_false = false; static constexpr bool always_false = false;
template<typename T>
concept Three_Way_Comparable = requires (T const& lhs, T const& rhs) {
{ lhs <=> rhs };
};
#endif #endif

View File

@ -81,7 +81,7 @@ static void encourage_contact(std::ostream &os)
<< pretty::end << std::flush; << pretty::end << std::flush;
} }
void assert(bool condition, std::string message, Location loc) void ensure(bool condition, std::string message, Location loc)
{ {
if (condition) return; if (condition) return;
#if Debug #if Debug

View File

@ -15,13 +15,8 @@
#include <musique/common.hh> #include <musique/common.hh>
#include <musique/location.hh> #include <musique/location.hh>
// To make sure, that we don't collide with <cassert> macro
#ifdef assert
#undef assert
#endif
/// Guards that program exits if condition does not hold /// Guards that program exits if condition does not hold
void assert(bool condition, std::string message, Location loc = Location::caller()); void ensure(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());

View File

@ -1,8 +1,8 @@
#include <sstream>
#include <musique/interpreter/env.hh> #include <musique/interpreter/env.hh>
#include <musique/format.hh> #include <musique/format.hh>
#include <musique/try.hh> #include <musique/try.hh>
#include <musique/value/value.hh> #include <musique/value/value.hh>
#include <sstream>
Result<std::string> format(Interpreter &i, Value const& value) Result<std::string> format(Interpreter &i, Value const& value)
{ {

View File

@ -14,7 +14,7 @@
void Interpreter::register_callbacks() void Interpreter::register_callbacks()
{ {
assert(callbacks == nullptr, "This field should be uninitialized"); ensure(callbacks == nullptr, "This field should be uninitialized");
callbacks = std::make_unique<Interpreter::Incoming_Midi_Callbacks>(); callbacks = std::make_unique<Interpreter::Incoming_Midi_Callbacks>();
callbacks->add_callbacks(*midi_connection, *this); callbacks->add_callbacks(*midi_connection, *this);
} }
@ -67,8 +67,8 @@ static inline std::optional<Error> create_chord(std::vector<Note> &chord, Interp
} }
if (auto arg_chord = get_if<Chord>(arg)) { if (auto arg_chord = get_if<Chord>(arg)) {
std::ranges::copy_if( std::copy_if(
arg_chord->notes, arg_chord->notes.begin(), arg_chord->notes.end(),
std::back_inserter(chord), std::back_inserter(chord),
[](Note const& n) { return n.base.has_value(); } [](Note const& n) { return n.base.has_value(); }
); );
@ -80,7 +80,7 @@ static inline std::optional<Error> create_chord(std::vector<Note> &chord, Interp
continue; continue;
} }
assert(false, "this type is not supported inside chord"); // TODO(assert) ensure(false, "this type is not supported inside chord"); // TODO(assert)
} }
return {}; return {};
@ -90,7 +90,7 @@ static inline std::optional<Error> create_chord(std::vector<Note> &chord, Interp
template<auto Mem_Ptr> template<auto Mem_Ptr>
static Result<Value> ctx_read_write_property(Interpreter &interpreter, std::vector<Value> args) static Result<Value> ctx_read_write_property(Interpreter &interpreter, std::vector<Value> args)
{ {
assert(args.size() <= 1, "Ctx get or set is only supported (wrong number of arguments)"); // TODO(assert) ensure(args.size() <= 1, "Ctx get or set is only supported (wrong number of arguments)"); // TODO(assert)
using Member_Type = std::remove_cvref_t<decltype(std::declval<Context>().*(Mem_Ptr))>; using Member_Type = std::remove_cvref_t<decltype(std::declval<Context>().*(Mem_Ptr))>;
@ -98,7 +98,7 @@ static Result<Value> ctx_read_write_property(Interpreter &interpreter, std::vect
return Number(interpreter.context_stack.back().*(Mem_Ptr)); return Number(interpreter.context_stack.back().*(Mem_Ptr));
} }
assert(std::holds_alternative<Number>(args.front().data), "Ctx only holds numeric values"); ensure(std::holds_alternative<Number>(args.front().data), "Ctx only holds numeric values");
if constexpr (std::is_same_v<Member_Type, Number>) { if constexpr (std::is_same_v<Member_Type, Number>) {
interpreter.context_stack.back().*(Mem_Ptr) = std::get<Number>(args.front().data); interpreter.context_stack.back().*(Mem_Ptr) = std::get<Number>(args.front().data);
@ -118,7 +118,7 @@ static Result<Array> into_flat_array(Interpreter &interpreter, std::span<Value>
for (auto &arg : args) { for (auto &arg : args) {
std::visit(Overloaded { std::visit(Overloaded {
[&target](Array &&array) -> std::optional<Error> { [&target](Array &&array) -> std::optional<Error> {
std::ranges::move(array.elements, std::back_inserter(target.elements)); std::move(array.elements.begin(), array.elements.end(), std::back_inserter(target.elements));
return {}; return {};
}, },
[&target, &interpreter](Block &&block) -> std::optional<Error> { [&target, &interpreter](Block &&block) -> std::optional<Error> {
@ -300,10 +300,10 @@ static inline Result<Value> builtin_play(Interpreter &i, Container args)
static Result<Value> builtin_par(Interpreter &i, std::vector<Value> args) { static Result<Value> builtin_par(Interpreter &i, std::vector<Value> args) {
Try(ensure_midi_connection_available(i, Midi_Connection_Type::Output, "par")); Try(ensure_midi_connection_available(i, Midi_Connection_Type::Output, "par"));
assert(args.size() >= 1, "par only makes sense for at least one argument"); // TODO(assert) ensure(args.size() >= 1, "par only makes sense for at least one argument"); // TODO(assert)
if (args.size() == 1) { if (args.size() == 1) {
auto chord = get_if<Chord>(args.front()); auto chord = get_if<Chord>(args.front());
assert(chord, "Par expects music value as first argument"); // TODO(assert) ensure(chord, "Par expects music value as first argument"); // TODO(assert)
Try(i.play(std::move(*chord))); Try(i.play(std::move(*chord)));
return Value{}; return Value{};
} }
@ -311,7 +311,7 @@ static Result<Value> builtin_par(Interpreter &i, std::vector<Value> args) {
// Create chord that should sustain during playing of all other notes // Create chord that should sustain during playing of all other notes
auto &ctx = i.context_stack.back(); auto &ctx = i.context_stack.back();
auto chord = get_if<Chord>(args.front()); auto chord = get_if<Chord>(args.front());
assert(chord, "par expects music value as first argument"); // TODO(assert) ensure(chord, "par expects music value as first argument"); // TODO(assert)
std::for_each(chord->notes.begin(), chord->notes.end(), [&](Note &note) { note = ctx.fill(note); }); std::for_each(chord->notes.begin(), chord->notes.end(), [&](Note &note) { note = ctx.fill(note); });
@ -674,7 +674,7 @@ static Result<Value> builtin_update(Interpreter &i, std::vector<Value> args)
/// Return typeof variable /// Return typeof variable
static Result<Value> builtin_typeof(Interpreter&, std::vector<Value> args) static Result<Value> builtin_typeof(Interpreter&, std::vector<Value> args)
{ {
assert(args.size() == 1, "typeof expects only one argument"); // TODO(assert) ensure(args.size() == 1, "typeof expects only one argument"); // TODO(assert)
return Symbol(type_name(args.front())); return Symbol(type_name(args.front()));
} }
@ -701,7 +701,7 @@ static Result<Value> builtin_shuffle(Interpreter &i, std::vector<Value> args)
{ {
static std::mt19937 rnd{std::random_device{}()}; static std::mt19937 rnd{std::random_device{}()};
auto array = Try(flatten(i, std::move(args))); auto array = Try(flatten(i, std::move(args)));
std::ranges::shuffle(array, rnd); std::shuffle(array.begin(), array.end(), rnd);
return array; return array;
} }
@ -709,7 +709,7 @@ static Result<Value> builtin_shuffle(Interpreter &i, std::vector<Value> args)
static Result<Value> builtin_permute(Interpreter &i, std::vector<Value> args) static Result<Value> builtin_permute(Interpreter &i, std::vector<Value> args)
{ {
auto array = Try(flatten(i, std::move(args))); auto array = Try(flatten(i, std::move(args)));
std::ranges::next_permutation(array); std::next_permutation(array.begin(), array.end());
return array; return array;
} }
@ -717,7 +717,7 @@ static Result<Value> builtin_permute(Interpreter &i, std::vector<Value> args)
static Result<Value> builtin_sort(Interpreter &i, std::vector<Value> args) static Result<Value> builtin_sort(Interpreter &i, std::vector<Value> args)
{ {
auto array = Try(flatten(i, std::move(args))); auto array = Try(flatten(i, std::move(args)));
std::ranges::sort(array); std::sort(array.begin(), array.end());
return array; return array;
} }
@ -725,7 +725,7 @@ static Result<Value> builtin_sort(Interpreter &i, std::vector<Value> args)
static Result<Value> builtin_reverse(Interpreter &i, std::vector<Value> args) static Result<Value> builtin_reverse(Interpreter &i, std::vector<Value> args)
{ {
auto array = Try(flatten(i, std::move(args))); auto array = Try(flatten(i, std::move(args)));
std::ranges::reverse(array); std::reverse(array.begin(), array.end());
return array; return array;
} }
@ -733,7 +733,7 @@ static Result<Value> builtin_reverse(Interpreter &i, std::vector<Value> args)
static Result<Value> builtin_min(Interpreter &i, std::vector<Value> args) static Result<Value> builtin_min(Interpreter &i, std::vector<Value> args)
{ {
auto array = Try(deep_flat(i, args)); auto array = Try(deep_flat(i, args));
if (auto min = std::ranges::min_element(array); min != array.end()) if (auto min = std::min_element(array.begin(), array.end()); min != array.end())
return *min; return *min;
return Value{}; return Value{};
} }
@ -742,7 +742,7 @@ static Result<Value> builtin_min(Interpreter &i, std::vector<Value> args)
static Result<Value> builtin_max(Interpreter &i, std::vector<Value> args) static Result<Value> builtin_max(Interpreter &i, std::vector<Value> args)
{ {
auto array = Try(deep_flat(i, args)); auto array = Try(deep_flat(i, args));
if (auto max = std::ranges::max_element(array); max != array.end()) if (auto max = std::max_element(array.begin(), array.end()); max != array.end())
return *max; return *max;
return Value{}; return Value{};
} }

View File

@ -5,6 +5,8 @@
#include <musique/try.hh> #include <musique/try.hh>
#include <musique/value/intrinsic.hh> #include <musique/value/intrinsic.hh>
#include <unordered_map>
/// Intrinsic implementation primitive to ease operation vectorization /// Intrinsic implementation primitive to ease operation vectorization
static Result<Value> vectorize(auto &&operation, Interpreter &interpreter, Value lhs, Value rhs) static Result<Value> vectorize(auto &&operation, Interpreter &interpreter, Value lhs, Value rhs)
{ {
@ -20,7 +22,7 @@ static Result<Value> vectorize(auto &&operation, Interpreter &interpreter, Value
return array; return array;
} }
assert(rhs_coll != nullptr, "Trying to vectorize two non-collections"); ensure(rhs_coll != nullptr, "Trying to vectorize two non-collections");
Array array; Array array;
for (auto i = 0u; i < rhs_coll->size(); ++i) { for (auto i = 0u; i < rhs_coll->size(); ++i) {
@ -34,7 +36,7 @@ static Result<Value> vectorize(auto &&operation, Interpreter &interpreter, Value
/// @invariant args.size() == 2 /// @invariant args.size() == 2
static Result<Value> vectorize(auto &&operation, Interpreter &interpreter, std::vector<Value> args) static Result<Value> vectorize(auto &&operation, Interpreter &interpreter, std::vector<Value> args)
{ {
assert(args.size() == 2, "Vectorization primitive only supports two arguments"); ensure(args.size() == 2, "Vectorization primitive only supports two arguments");
return vectorize(std::move(operation), interpreter, std::move(args.front()), std::move(args.back())); return vectorize(std::move(operation), interpreter, std::move(args.front()), std::move(args.back()));
} }

View File

@ -7,7 +7,7 @@ std::shared_ptr<Env> Env::global = nullptr;
std::shared_ptr<Env> Env::make() std::shared_ptr<Env> Env::make()
{ {
auto new_env = new Env(); auto new_env = new Env();
assert(new_env, "Cannot construct new env"); ensure(new_env, "Cannot construct new env");
return std::shared_ptr<Env>(new_env); return std::shared_ptr<Env>(new_env);
} }

View File

@ -35,7 +35,7 @@ Interpreter::Interpreter()
context_stack.emplace_back(); context_stack.emplace_back();
// Environment initlialization // Environment initlialization
assert(!bool(Env::global), "Only one instance of interpreter can be at one time"); ensure(!bool(Env::global), "Only one instance of interpreter can be at one time");
env = Env::global = Env::make(); env = Env::global = Env::make();
// Builtins initialization // Builtins initialization
@ -83,16 +83,16 @@ Result<Value> Interpreter::eval(Ast &&ast)
case Ast::Type::Binary: case Ast::Type::Binary:
{ {
assert(ast.arguments.size() == 2, "Expected arguments of binary operation to be 2 long"); ensure(ast.arguments.size() == 2, "Expected arguments of binary operation to be 2 long");
if (ast.token.source == "=") { if (ast.token.source == "=") {
auto lhs = std::move(ast.arguments.front()); auto lhs = std::move(ast.arguments.front());
auto rhs = std::move(ast.arguments.back()); auto rhs = std::move(ast.arguments.back());
assert(lhs.type == Ast::Type::Literal && lhs.token.type == Token::Type::Symbol, ensure(lhs.type == Ast::Type::Literal && lhs.token.type == Token::Type::Symbol,
"Currently LHS of assigment must be an identifier"); // TODO(assert) "Currently LHS of assigment must be an identifier"); // TODO(assert)
Value *v = env->find(std::string(lhs.token.source)); Value *v = env->find(std::string(lhs.token.source));
assert(v, "Cannot resolve variable: "s + std::string(lhs.token.source)); // TODO(assert) ensure(v, "Cannot resolve variable: "s + std::string(lhs.token.source)); // TODO(assert)
return *v = Try(eval(std::move(rhs)).with_location(ast.token.location)); return *v = Try(eval(std::move(rhs)).with_location(ast.token.location));
} }
@ -124,11 +124,11 @@ Result<Value> Interpreter::eval(Ast &&ast)
auto lhs = std::move(ast.arguments.front()); auto lhs = std::move(ast.arguments.front());
auto rhs = std::move(ast.arguments.back()); auto rhs = std::move(ast.arguments.back());
auto const rhs_loc = rhs.location; auto const rhs_loc = rhs.location;
assert(lhs.type == Ast::Type::Literal && lhs.token.type == Token::Type::Symbol, ensure(lhs.type == Ast::Type::Literal && lhs.token.type == Token::Type::Symbol,
"Currently LHS of assigment must be an identifier"); // TODO(assert) "Currently LHS of assigment must be an identifier"); // TODO(assert)
Value *v = env->find(std::string(lhs.token.source)); Value *v = env->find(std::string(lhs.token.source));
assert(v, "Cannot resolve variable: "s + std::string(lhs.token.source)); // TODO(assert) ensure(v, "Cannot resolve variable: "s + std::string(lhs.token.source)); // TODO(assert)
return *v = Try(op->second(*this, { return *v = Try(op->second(*this, {
*v, Try(eval(std::move(rhs)).with_location(rhs_loc)) *v, Try(eval(std::move(rhs)).with_location(rhs_loc))
}).with_location(ast.token.location)); }).with_location(ast.token.location));
@ -179,9 +179,9 @@ Result<Value> Interpreter::eval(Ast &&ast)
case Ast::Type::Variable_Declaration: case Ast::Type::Variable_Declaration:
{ {
assert(ast.arguments.size() == 2, "Only simple assigments are supported now"); ensure(ast.arguments.size() == 2, "Only simple assigments are supported now");
assert(ast.arguments.front().type == Ast::Type::Literal, "Only names are supported as LHS arguments now"); ensure(ast.arguments.front().type == Ast::Type::Literal, "Only names are supported as LHS arguments now");
assert(ast.arguments.front().token.type == Token::Type::Symbol, "Only names are supported as LHS arguments now"); ensure(ast.arguments.front().token.type == Token::Type::Symbol, "Only names are supported as LHS arguments now");
env->force_define(std::string(ast.arguments.front().token.source), Try(eval(std::move(ast.arguments.back())))); env->force_define(std::string(ast.arguments.front().token.source), Try(eval(std::move(ast.arguments.back()))));
return Value{}; return Value{};
} }
@ -191,10 +191,10 @@ Result<Value> Interpreter::eval(Ast &&ast)
{ {
Block block; Block block;
if (ast.type == Ast::Type::Lambda) { if (ast.type == Ast::Type::Lambda) {
auto parameters = std::span(ast.arguments.begin(), std::prev(ast.arguments.end())); auto parameters = std::span<Ast>(ast.arguments.data(), ast.arguments.size() - 1);
block.parameters.reserve(parameters.size()); block.parameters.reserve(parameters.size());
for (auto &param : parameters) { for (auto &param : parameters) {
assert(param.type == Ast::Type::Literal && param.token.type == Token::Type::Symbol, "Not a name in parameter section of Ast::lambda"); ensure(param.type == Ast::Type::Literal && param.token.type == Token::Type::Symbol, "Not a name in parameter section of Ast::lambda");
block.parameters.push_back(std::string(std::move(param).token.source)); block.parameters.push_back(std::string(std::move(param).token.source));
} }
} }
@ -217,7 +217,7 @@ void Interpreter::enter_scope()
void Interpreter::leave_scope() void Interpreter::leave_scope()
{ {
assert(env != Env::global, "Cannot leave global scope"); ensure(env != Env::global, "Cannot leave global scope");
env = env->leave(); env = env->leave();
} }

View File

@ -4,6 +4,7 @@
#include <musique/midi/midi.hh> #include <musique/midi/midi.hh>
#include <musique/interpreter/context.hh> #include <musique/interpreter/context.hh>
#include <musique/value/value.hh> #include <musique/value/value.hh>
#include <unordered_map>
/// Given program tree evaluates it into Value /// Given program tree evaluates it into Value
struct Interpreter struct Interpreter

View File

@ -226,7 +226,7 @@ auto Lexer::consume_if(auto first, auto second) -> bool
void Lexer::rewind() void Lexer::rewind()
{ {
assert(last_rune_length != 0, "cannot rewind to not existing rune"); ensure(last_rune_length != 0, "cannot rewind to not existing rune");
source = { source.data() - last_rune_length, source.size() + last_rune_length }; source = { source.data() - last_rune_length, source.size() + last_rune_length };
token_length -= last_rune_length; token_length -= last_rune_length;
location = prev_location; location = prev_location;

View File

@ -23,7 +23,7 @@ void Lines::add_file(std::string filename, std::string_view source)
void Lines::add_line(std::string const& filename, std::string_view source, unsigned line_number) void Lines::add_line(std::string const& filename, std::string_view source, unsigned line_number)
{ {
assert(line_number != 0, "Line number = 0 is invalid"); ensure(line_number != 0, "Line number = 0 is invalid");
if (lines[filename].size() <= line_number) if (lines[filename].size() <= line_number)
lines[filename].resize(line_number); lines[filename].resize(line_number);
lines[filename][line_number - 1] = source; lines[filename][line_number - 1] = source;

View File

@ -5,6 +5,7 @@
#include <span> #include <span>
#include <thread> #include <thread>
#include <cstring> #include <cstring>
#include <cstdio>
#include <musique/format.hh> #include <musique/format.hh>
#include <musique/interpreter/env.hh> #include <musique/interpreter/env.hh>
@ -21,6 +22,7 @@ extern "C" {
#include <io.h> #include <io.h>
} }
#else #else
#include <unistd.h>
extern "C" { extern "C" {
#include <bestline.h> #include <bestline.h>
} }
@ -126,15 +128,13 @@ struct Runner
midi::Rt_Midi midi; midi::Rt_Midi midi;
Interpreter interpreter; Interpreter interpreter;
std::thread midi_input_event_loop;
std::stop_source stop_source;
/// Setup interpreter and midi connection with given port /// Setup interpreter and midi connection with given port
Runner(std::optional<unsigned> input_port, std::optional<unsigned> output_port) Runner(std::optional<unsigned> input_port, std::optional<unsigned> output_port)
: midi() : midi()
, interpreter{} , interpreter{}
{ {
assert(the == nullptr, "Only one instance of runner is supported"); ensure(the == nullptr, "Only one instance of runner is supported");
the = this; the = this;
bool const midi_go = bool(input_port) || bool(output_port); bool const midi_go = bool(input_port) || bool(output_port);
@ -149,11 +149,6 @@ struct Runner
std::cout << "Connected MIDI input to port " << *input_port << ". Ready for incoming messages!" << std::endl; std::cout << "Connected MIDI input to port " << *input_port << ". Ready for incoming messages!" << std::endl;
midi.connect_input(*input_port); midi.connect_input(*input_port);
} }
if (midi_go) {
interpreter.register_callbacks();
midi_input_event_loop = std::thread([this] { handle_midi_event_loop(); });
midi_input_event_loop.detach();
}
Env::global->force_define("say", +[](Interpreter &interpreter, std::vector<Value> args) -> Result<Value> { Env::global->force_define("say", +[](Interpreter &interpreter, std::vector<Value> args) -> Result<Value> {
for (auto it = args.begin(); it != args.end(); ++it) { for (auto it = args.begin(); it != args.end(); ++it) {
@ -166,21 +161,11 @@ struct Runner
}); });
} }
~Runner()
{
stop_source.request_stop();
}
Runner(Runner const&) = delete; Runner(Runner const&) = delete;
Runner(Runner &&) = delete; Runner(Runner &&) = delete;
Runner& operator=(Runner const&) = delete; Runner& operator=(Runner const&) = delete;
Runner& operator=(Runner &&) = delete; Runner& operator=(Runner &&) = delete;
void handle_midi_event_loop()
{
midi.input_event_loop(stop_source.get_token());
}
/// Run given source /// Run given source
std::optional<Error> run(std::string_view source, std::string_view filename, bool output = false) std::optional<Error> run(std::string_view source, std::string_view filename, bool output = false)
{ {
@ -221,7 +206,7 @@ bool is_tty()
#ifdef _WIN32 #ifdef _WIN32
return _isatty(STDOUT_FILENO); return _isatty(STDOUT_FILENO);
#else #else
return isatty(STDOUT_FILENO); return isatty(fileno(stdout));
#endif #endif
} }

View File

@ -3,7 +3,6 @@
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <optional> #include <optional>
#include <stop_token>
#include <string> #include <string>
// Documentation of midi messages available at http://midi.teragonaudio.com/tech/midispec.htm // Documentation of midi messages available at http://midi.teragonaudio.com/tech/midispec.htm
@ -48,8 +47,6 @@ namespace midi
void send_program_change(uint8_t channel, uint8_t program) override; void send_program_change(uint8_t channel, uint8_t program) override;
void send_controller_change(uint8_t channel, uint8_t controller_number, uint8_t value) override; void send_controller_change(uint8_t channel, uint8_t controller_number, uint8_t value) override;
void input_event_loop(std::stop_token);
std::optional<RtMidiIn> input; std::optional<RtMidiIn> input;
std::optional<RtMidiOut> output; std::optional<RtMidiOut> output;
}; };

View File

@ -36,9 +36,11 @@ try {
void midi::Rt_Midi::connect_output(unsigned target) void midi::Rt_Midi::connect_output(unsigned target)
try { try {
assert(not output.has_value(), "Reconeccting is not supported yet"); ensure(not output.has_value(), "Reconeccting is not supported yet");
output.emplace(); output.emplace();
output->openPort(target, "Musique output port"); output->openVirtualPort("Musique output port");
// output->openPort(target, "Musique output port");
} catch (RtMidiError &error) { } catch (RtMidiError &error) {
// TODO(error) // TODO(error)
std::cerr << "Failed to use MIDI connection: " << error.getMessage() << std::endl; std::cerr << "Failed to use MIDI connection: " << error.getMessage() << std::endl;
@ -47,7 +49,7 @@ try {
void midi::Rt_Midi::connect_input(unsigned target) void midi::Rt_Midi::connect_input(unsigned target)
try { try {
assert(not input.has_value(), "Reconeccting is not supported yet"); ensure(not input.has_value(), "Reconeccting is not supported yet");
input.emplace(); input.emplace();
input->openPort(target, "Musique input port"); input->openPort(target, "Musique input port");
} catch (RtMidiError &error) { } catch (RtMidiError &error) {
@ -104,6 +106,3 @@ void midi::Rt_Midi::send_controller_change(uint8_t channel, uint8_t controller_n
send_message(*output, std::array { std::uint8_t(Control_Change + channel), controller_number, value }); send_message(*output, std::array { std::uint8_t(Control_Change + channel), controller_number, value });
} }
void midi::Rt_Midi::input_event_loop(std::stop_token)
{
}

View File

@ -118,7 +118,7 @@ Result<Ast> Parser::parse_variable_declaration()
return std::move(lvalue).error(); return std::move(lvalue).error();
} }
assert(expect(Token::Type::Operator, ":="), "This function should always be called with valid sequence"); ensure(expect(Token::Type::Operator, ":="), "This function should always be called with valid sequence");
consume(); consume();
return Ast::variable_declaration(lvalue->location, { *std::move(lvalue) }, Try(parse_expression())); return Ast::variable_declaration(lvalue->location, { *std::move(lvalue) }, Try(parse_expression()));
} }
@ -133,7 +133,7 @@ Result<Ast> Parser::parse_infix_expression()
|| expect(Token::Type::Keyword, "or"); || expect(Token::Type::Keyword, "or");
if (next_is_operator) { if (next_is_operator) {
assert(not expect(Token::Type::Operator, "."), "This should be handled by parse_index_expression"); ensure(not expect(Token::Type::Operator, "."), "This should be handled by parse_index_expression");
auto op = consume(); auto op = consume();
Ast ast; Ast ast;
@ -161,7 +161,7 @@ Result<Ast> Parser::parse_rhs_of_infix_expression(Ast lhs)
return lhs; return lhs;
} }
assert(not expect(Token::Type::Operator, "."), "This should be handled by parse_index_expression"); ensure(not expect(Token::Type::Operator, "."), "This should be handled by parse_index_expression");
auto op = consume(); auto op = consume();
if (precedense(lhs.token.source) >= precedense(op.source)) { if (precedense(lhs.token.source) >= precedense(op.source)) {
@ -249,7 +249,7 @@ Result<Ast> Parser::parse_atomic_expression()
if (not expect(Token::Type::Close_Block)) { if (not expect(Token::Type::Close_Block)) {
if (expect(Token::Type::Parameter_Separator)) { if (expect(Token::Type::Parameter_Separator)) {
if (is_lambda) { if (is_lambda) {
assert(false, "There should be error message that you cannot put multiple parameter separators in one block"); ensure(false, "There should be error message that you cannot put multiple parameter separators in one block");
} else { } else {
// This may be a result of user trying to specify parameters from things that cannot be parameters (like chord literals) // This may be a result of user trying to specify parameters from things that cannot be parameters (like chord literals)
// or accidential hit of "|" on the keyboard. We can detect first case by ensuring that all tokens between this place // or accidential hit of "|" on the keyboard. We can detect first case by ensuring that all tokens between this place
@ -467,7 +467,7 @@ Ast Ast::binary(Token token, Ast lhs, Ast rhs)
Ast Ast::call(std::vector<Ast> call) Ast Ast::call(std::vector<Ast> call)
{ {
assert(!call.empty(), "Call must have at least pice of code that is beeing called"); ensure(!call.empty(), "Call must have at least pice of code that is beeing called");
Ast ast; Ast ast;
ast.type = Type::Call; ast.type = Type::Call;

View File

@ -46,13 +46,13 @@ struct Try_Traits<std::optional<Error>>
static std::nullopt_t yield_value(std::optional<Error>&& err) static std::nullopt_t yield_value(std::optional<Error>&& err)
{ {
assert(not err.has_value(), "Trying to yield value from optional that contains error"); ensure(not err.has_value(), "Trying to yield value from optional that contains error");
return std::nullopt; return std::nullopt;
} }
static Error yield_error(std::optional<Error>&& err) static Error yield_error(std::optional<Error>&& err)
{ {
assert(err.has_value(), "Trying to yield value from optional that NOT constains error"); ensure(err.has_value(), "Trying to yield value from optional that NOT constains error");
return std::move(*err); return std::move(*err);
} }
}; };
@ -70,7 +70,7 @@ struct Try_Traits<Result<T>>
static auto yield_value(Result<T> val) static auto yield_value(Result<T> val)
{ {
assert(val.has_value(), "Trying to yield value from expected that contains error"); ensure(val.has_value(), "Trying to yield value from expected that contains error");
if constexpr (std::is_void_v<T>) { if constexpr (std::is_void_v<T>) {
} else { } else {
return std::move(*val); return std::move(*val);
@ -79,7 +79,7 @@ struct Try_Traits<Result<T>>
static Error yield_error(Result<T>&& val) static Error yield_error(Result<T>&& val)
{ {
assert(not val.has_value(), "Trying to yield error from expected with value"); ensure(not val.has_value(), "Trying to yield error from expected with value");
return std::move(val.error()); return std::move(val.error());
} }
}; };

View File

@ -16,7 +16,7 @@ static inline std::optional<Error> guard_index(unsigned index, unsigned size)
// TODO Add memoization // TODO Add memoization
Result<Value> Block::index(Interpreter &i, unsigned position) const Result<Value> Block::index(Interpreter &i, unsigned position) const
{ {
assert(parameters.size() == 0, "cannot index into block with parameters (for now)"); ensure(parameters.size() == 0, "cannot index into block with parameters (for now)");
if (body.type != Ast::Type::Sequence) { if (body.type != Ast::Type::Sequence) {
Try(guard_index(position, 1)); Try(guard_index(position, 1));
return i.eval((Ast)body); return i.eval((Ast)body);

View File

@ -17,7 +17,7 @@ Chord::Chord(std::vector<Note> &&notes)
Chord Chord::from(std::string_view source) Chord Chord::from(std::string_view source)
{ {
auto note = Note::from(source); auto note = Note::from(source);
assert(note.has_value(), "don't know how this could happen"); ensure(note.has_value(), "don't know how this could happen");
Chord chord; Chord chord;
source.remove_prefix(1 + (source[1] == '#')); source.remove_prefix(1 + (source[1] == '#'));
@ -120,7 +120,7 @@ Result<Value> Chord::operator()(Interpreter& interpreter, std::vector<Value> arg
std::move(current.begin(), current.end(), std::back_inserter(array)); std::move(current.begin(), current.end(), std::back_inserter(array));
assert(not array.empty(), "At least *this should be in this array"); ensure(not array.empty(), "At least *this should be in this array");
return array; return array;
} }

View File

@ -12,6 +12,7 @@ struct Function
virtual Result<Value> operator()(Interpreter &i, std::vector<Value> params) const = 0; virtual Result<Value> operator()(Interpreter &i, std::vector<Value> params) const = 0;
constexpr bool operator==(Function const&) const = default; constexpr bool operator==(Function const&) const = default;
constexpr std::strong_ordering operator<=>(Function const&) const = default;
}; };
#endif // MUSIQUE_VALUE_FUNCTION_HH #endif // MUSIQUE_VALUE_FUNCTION_HH

View File

@ -26,6 +26,8 @@ struct Intrinsic : Function
/// Compares if function pointers are equal /// Compares if function pointers are equal
bool operator==(Intrinsic const&) const = default; bool operator==(Intrinsic const&) const = default;
std::strong_ordering operator<=>(Intrinsic const& rhs) const = default;
}; };
#endif // MUSIQUE_VALUE_INTRINSIC_HH #endif // MUSIQUE_VALUE_INTRINSIC_HH

View File

@ -70,7 +70,7 @@ std::optional<u8> Note::into_midi_note() const
u8 Note::into_midi_note(i8 default_octave) const u8 Note::into_midi_note(i8 default_octave) const
{ {
assert(bool(this->base), "Pause don't translate into MIDI"); ensure(bool(this->base), "Pause don't translate into MIDI");
auto const octave = this->octave.has_value() ? *this->octave : default_octave; auto const octave = this->octave.has_value() ? *this->octave : default_octave;
// octave is in range [-1, 9] where Note { .base = 0, .octave = -1 } is midi note 0 // octave is in range [-1, 9] where Note { .base = 0, .octave = -1 } is midi note 0
return (octave + 1) * 12 + *base; return (octave + 1) * 12 + *base;

View File

@ -19,7 +19,7 @@ auto Number::as_int() const -> i64
{ {
// We don't perform GCD simplification in place due to constness // We don't perform GCD simplification in place due to constness
auto self = simplify(); auto self = simplify();
assert(self.den == 1 || self.num == 0, "Implicit coarce to integer while holding fractional number is not allowed"); ensure(self.den == 1 || self.num == 0, "Implicit coarce to integer while holding fractional number is not allowed");
return self.num; return self.num;
} }
@ -203,7 +203,7 @@ static consteval auto compute_powers(Number::value_type multiplier) -> std::arra
static Number::value_type pow10(usize n) static Number::value_type pow10(usize n)
{ {
static constexpr auto Powers = compute_powers(10); static constexpr auto Powers = compute_powers(10);
assert(n < Powers.size(), "Trying to compute power of 10 grater then current type can hold"); ensure(n < Powers.size(), "Trying to compute power of 10 grater then current type can hold");
return Powers[n]; return Powers[n];
} }

View File

@ -1,4 +1,5 @@
#include <musique/accessors.hh> #include <musique/accessors.hh>
#include <musique/algo.hh>
#include <musique/guard.hh> #include <musique/guard.hh>
#include <musique/interpreter/env.hh> #include <musique/interpreter/env.hh>
#include <musique/interpreter/interpreter.hh> #include <musique/interpreter/interpreter.hh>
@ -7,9 +8,25 @@
#include <iostream> #include <iostream>
#include <numeric> #include <numeric>
#include <compare>
Value::Value() = default; Value::Value() = default;
std::strong_ordering operator<=>(std::string const& lhs, std::string const& rhs)
{
if (auto cmp = lhs.size() <=> rhs.size(); cmp == 0) {
if (auto cmp = strncmp(lhs.c_str(), rhs.c_str(), lhs.size()); cmp == 0) {
return std::strong_ordering::equal;
} else if (cmp < 0) {
return std::strong_ordering::less;
} else {
return std::strong_ordering::greater;
}
} else {
return cmp;
}
}
Result<Value> Value::from(Token t) Result<Value> Value::from(Token t)
{ {
switch (t.type) { switch (t.type) {
@ -28,7 +45,7 @@ Result<Value> Value::from(Token t)
case Token::Type::Chord: case Token::Type::Chord:
if (t.source.size() == 1 || (t.source.size() == 2 && t.source.back() == '#')) { if (t.source.size() == 1 || (t.source.size() == 2 && t.source.back() == '#')) {
auto maybe_note = Note::from(t.source); auto maybe_note = Note::from(t.source);
assert(maybe_note.has_value(), "Somehow parser passed invalid note literal"); ensure(maybe_note.has_value(), "Somehow parser passed invalid note literal");
return *maybe_note; return *maybe_note;
} }
@ -104,7 +121,7 @@ Result<Value> Value::index(Interpreter &i, unsigned position) const
if (auto collection = get_if<Collection>(data)) { if (auto collection = get_if<Collection>(data)) {
return collection->index(i, position); return collection->index(i, position);
} }
assert(false, "Block indexing is not supported for this type"); // TODO(assert) ensure(false, "Block indexing is not supported for this type"); // TODO(assert)
unreachable(); unreachable();
} }
@ -141,7 +158,7 @@ usize Value::size() const
return collection->size(); return collection->size();
} }
assert(false, "This type does not support Value::size()"); // TODO(assert) ensure(false, "This type does not support Value::size()"); // TODO(assert)
unreachable(); unreachable();
} }
@ -151,18 +168,12 @@ std::partial_ordering Value::operator<=>(Value const& rhs) const
return std::visit(Overloaded { return std::visit(Overloaded {
[](Nil, Nil) { return std::partial_ordering::equivalent; }, [](Nil, Nil) { return std::partial_ordering::equivalent; },
[](Array const& lhs, Array const& rhs) { [](Array const& lhs, Array const& rhs) {
return std::lexicographical_compare_three_way( return algo::lexicographical_compare(lhs.elements, rhs.elements);
lhs.elements.begin(), lhs.elements.end(),
rhs.elements.begin(), rhs.elements.end()
);
}, },
[](Chord const& lhs, Chord const& rhs) { [](Chord const& lhs, Chord const& rhs) {
return std::lexicographical_compare_three_way( return algo::lexicographical_compare(lhs.notes, rhs.notes);
lhs.notes.begin(), lhs.notes.end(),
rhs.notes.begin(), rhs.notes.end()
);
}, },
[]<typename T>(T const& lhs, T const& rhs) -> std::partial_ordering requires std::three_way_comparable<T> { []<typename T>(T const& lhs, T const& rhs) -> std::partial_ordering requires Three_Way_Comparable<T> {
return lhs <=> rhs; return lhs <=> rhs;
}, },
[](auto&&...) { return std::partial_ordering::unordered; } [](auto&&...) { return std::partial_ordering::unordered; }