Merge pull request 'Adding support for MacOS' (#36) from macos into main
This commit is contained in:
commit
480de4182c
@ -12,9 +12,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
* Added `scan` builtin, which computes prefix sum of passed values when provided with addition operator
|
* Added `scan` builtin, which computes prefix sum of passed values when provided with addition operator
|
||||||
* Added [rtmidi](https://github.com/thestk/rtmidi/) dependency which should provide multiplatform MIDI support
|
* Added [rtmidi](https://github.com/thestk/rtmidi/) dependency which should provide multiplatform MIDI support
|
||||||
* Support for Windows (with only basic REPL) (`make os=windows`)
|
* Support for Windows (with only basic REPL) (`make os=windows`)
|
||||||
|
* Support for MacOS (`make os=macos`)
|
||||||
* Release package now with compiled Windows binary
|
* Release package now with compiled Windows binary
|
||||||
* `:load` REPL command to load Musique files inside Musique session. Allows for delayed file execution after a connection
|
* `:load` REPL command to load Musique files inside Musique session. Allows for delayed file execution after a connection
|
||||||
* `:quit` REPL command that mirrors `:exit` command
|
* `:quit` REPL command that mirrors `:exit` command
|
||||||
|
* Virtual MIDI output port creation as default action (--output connects to existing one)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
22
Makefile
22
Makefile
@ -3,17 +3,25 @@ include config.mk
|
|||||||
Sources := $(shell find musique/ -name '*.cc')
|
Sources := $(shell find musique/ -name '*.cc')
|
||||||
Obj := $(subst musique/,,$(Sources:%.cc=%.o))
|
Obj := $(subst musique/,,$(Sources:%.cc=%.o))
|
||||||
|
|
||||||
|
ifeq ($(os),windows)
|
||||||
|
all: bin/musique.exe
|
||||||
|
debug: bin/windows/debug/musique.exe
|
||||||
|
else
|
||||||
all: bin/musique
|
all: bin/musique
|
||||||
|
debug: bin/$(os)/debug/musique
|
||||||
|
endif
|
||||||
|
|
||||||
include scripts/debug.mk
|
include scripts/$(os).mk
|
||||||
include scripts/release.mk
|
include scripts/build.mk
|
||||||
include scripts/test.mk
|
include scripts/test.mk
|
||||||
include scripts/windows.mk
|
|
||||||
|
bin/$(Target): bin/$(os)/$(Target)
|
||||||
|
ln -f $< $@
|
||||||
|
|
||||||
# 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/$(os)/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
|
||||||
@ -32,5 +40,5 @@ install: bin/musique
|
|||||||
|
|
||||||
.PHONY: clean doc doc-open all test unit-tests release install
|
.PHONY: clean doc doc-open all test unit-tests release install
|
||||||
|
|
||||||
$(shell mkdir -p $(subst musique/,bin/,$(shell find musique/* -type d)))
|
$(shell mkdir -p $(subst musique/,bin/$(os)/,$(shell find musique/* -type d)))
|
||||||
$(shell mkdir -p $(subst musique/,bin/debug/,$(shell find musique/* -type d)))
|
$(shell mkdir -p $(subst musique/,bin/$(os)/debug/,$(shell find musique/* -type d)))
|
||||||
|
16
config.mk
16
config.mk
@ -1,22 +1,16 @@
|
|||||||
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
|
||||||
|
|
||||||
ifeq ($(os),windows)
|
ifeq ($(shell uname),Darwin)
|
||||||
CC=i686-w64-mingw32-gcc
|
os=macos
|
||||||
CXX=i686-w64-mingw32-g++
|
|
||||||
CPPFLAGS:=$(CPPFLAGS) -D__WINDOWS_MM__
|
|
||||||
LDLIBS:=-lwinmm $(LDLIBS)
|
|
||||||
else
|
else
|
||||||
CC=gcc
|
os=linux
|
||||||
CXX=g++
|
|
||||||
CPPFLAGS:=$(CPPFLAGS) -D __LINUX_ALSA__
|
|
||||||
LDLIBS:=-lasound $(LDLIBS)
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -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]`
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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());
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#include <musique/algo.hh>
|
#include <musique/algo.hh>
|
||||||
#include <musique/interpreter/env.hh>
|
#include <musique/interpreter/env.hh>
|
||||||
#include <musique/guard.hh>
|
#include <musique/guard.hh>
|
||||||
#include <musique/interpreter/incoming_midi.hh>
|
|
||||||
#include <musique/interpreter/interpreter.hh>
|
#include <musique/interpreter/interpreter.hh>
|
||||||
#include <musique/try.hh>
|
#include <musique/try.hh>
|
||||||
|
|
||||||
@ -12,13 +11,6 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
void Interpreter::register_callbacks()
|
|
||||||
{
|
|
||||||
assert(callbacks == nullptr, "This field should be uninitialized");
|
|
||||||
callbacks = std::make_unique<Interpreter::Incoming_Midi_Callbacks>();
|
|
||||||
callbacks->add_callbacks(*midi_connection, *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if type has index method
|
/// Check if type has index method
|
||||||
template<typename T>
|
template<typename T>
|
||||||
concept With_Index_Method = requires (T &t, Interpreter interpreter, usize position) {
|
concept With_Index_Method = requires (T &t, Interpreter interpreter, usize position) {
|
||||||
@ -67,8 +59,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 +72,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 +82,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 +90,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 +110,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> {
|
||||||
@ -276,7 +268,7 @@ static std::optional<Error> action_play(Interpreter &i, Value v)
|
|||||||
template<With_Index_Operator Container = std::vector<Value>>
|
template<With_Index_Operator Container = std::vector<Value>>
|
||||||
static inline Result<Value> builtin_play(Interpreter &i, Container args)
|
static inline Result<Value> builtin_play(Interpreter &i, Container args)
|
||||||
{
|
{
|
||||||
Try(ensure_midi_connection_available(i, Midi_Connection_Type::Output, "play"));
|
Try(ensure_midi_connection_available(i, "play"));
|
||||||
auto previous_action = std::exchange(i.default_action, action_play);
|
auto previous_action = std::exchange(i.default_action, action_play);
|
||||||
i.context_stack.push_back(i.context_stack.back());
|
i.context_stack.push_back(i.context_stack.back());
|
||||||
|
|
||||||
@ -298,12 +290,12 @@ static inline Result<Value> builtin_play(Interpreter &i, Container args)
|
|||||||
|
|
||||||
/// Play first argument while playing all others
|
/// Play first argument while playing all others
|
||||||
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, "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 +303,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 ¬e) { note = ctx.fill(note); });
|
std::for_each(chord->notes.begin(), chord->notes.end(), [&](Note ¬e) { note = ctx.fill(note); });
|
||||||
|
|
||||||
@ -674,7 +666,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 +693,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 +701,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 +709,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 +717,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 +725,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 +734,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{};
|
||||||
}
|
}
|
||||||
@ -911,28 +903,6 @@ static Result<Value> builtin_note_off(Interpreter &i, std::vector<Value> args)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add handler for incoming midi messages
|
|
||||||
static Result<Value> builtin_incoming(Interpreter &i, std::vector<Value> args)
|
|
||||||
{
|
|
||||||
if (auto a = match<Symbol, Function>(args)) {
|
|
||||||
auto& [symbol, fun] = *a;
|
|
||||||
if (symbol == "note_on" || symbol == "noteon") {
|
|
||||||
i.callbacks->note_on = std::move(args[1]);
|
|
||||||
} else if (symbol == "note_off" || symbol == "noteoff") {
|
|
||||||
i.callbacks->note_off = std::move(args[1]);
|
|
||||||
} else {
|
|
||||||
|
|
||||||
}
|
|
||||||
return Value{};
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors::Unsupported_Types_For {
|
|
||||||
.type = errors::Unsupported_Types_For::Function,
|
|
||||||
.name = "incoming",
|
|
||||||
.possibilities = { "(symbol, function) -> nil" }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Interleaves arguments
|
/// Interleaves arguments
|
||||||
static Result<Value> builtin_mix(Interpreter &i, std::vector<Value> args)
|
static Result<Value> builtin_mix(Interpreter &i, std::vector<Value> args)
|
||||||
{
|
{
|
||||||
@ -999,7 +969,6 @@ void Interpreter::register_builtin_functions()
|
|||||||
global.force_define("for", builtin_for);
|
global.force_define("for", builtin_for);
|
||||||
global.force_define("hash", builtin_hash);
|
global.force_define("hash", builtin_hash);
|
||||||
global.force_define("if", builtin_if);
|
global.force_define("if", builtin_if);
|
||||||
global.force_define("incoming", builtin_incoming);
|
|
||||||
global.force_define("instrument", builtin_program_change);
|
global.force_define("instrument", builtin_program_change);
|
||||||
global.force_define("len", builtin_len);
|
global.force_define("len", builtin_len);
|
||||||
global.force_define("max", builtin_max);
|
global.force_define("max", builtin_max);
|
||||||
|
@ -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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
#ifndef MUSIQUE_INCOMING_MIDI
|
|
||||||
#define MUSIQUE_INCOMING_MIDI
|
|
||||||
|
|
||||||
#include <musique/interpreter/interpreter.hh>
|
|
||||||
|
|
||||||
struct Interpreter::Incoming_Midi_Callbacks
|
|
||||||
{
|
|
||||||
Value note_on{};
|
|
||||||
Value note_off{};
|
|
||||||
|
|
||||||
inline Incoming_Midi_Callbacks() = default;
|
|
||||||
|
|
||||||
Incoming_Midi_Callbacks(Incoming_Midi_Callbacks &&) = delete;
|
|
||||||
Incoming_Midi_Callbacks(Incoming_Midi_Callbacks const&) = delete;
|
|
||||||
|
|
||||||
Incoming_Midi_Callbacks& operator=(Incoming_Midi_Callbacks &&) = delete;
|
|
||||||
Incoming_Midi_Callbacks& operator=(Incoming_Midi_Callbacks const&) = delete;
|
|
||||||
|
|
||||||
|
|
||||||
inline void add_callbacks(midi::Connection &midi, Interpreter &interpreter)
|
|
||||||
{
|
|
||||||
register_callback(midi.note_on_callback, note_on, interpreter);
|
|
||||||
register_callback(midi.note_off_callback, note_off, interpreter);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ...T>
|
|
||||||
inline void register_callback(std::function<void(T...)> &target, Value &callback, Interpreter &i)
|
|
||||||
{
|
|
||||||
if (&callback == ¬e_on || &callback == ¬e_off) {
|
|
||||||
// This messages have MIDI note number as second value, so they should be represented
|
|
||||||
// in our own note abstraction, not as numbers.
|
|
||||||
target = [interpreter = &i, callback = &callback](T ...source_args)
|
|
||||||
{
|
|
||||||
if (!std::holds_alternative<Nil>(callback->data)) {
|
|
||||||
std::vector<Value> args { Number(source_args)... };
|
|
||||||
args[1] = Note {
|
|
||||||
.base = i32(std::get<Number>(args[1].data).num % 12),
|
|
||||||
.octave = std::get<Number>(args[1].data).num / 12
|
|
||||||
};
|
|
||||||
auto result = (*callback)(*interpreter, std::move(args));
|
|
||||||
// We discard this since callback is running in another thread.
|
|
||||||
(void) result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Generic case, preserve all passed parameters as numbers
|
|
||||||
target = [interpreter = &i, callback = &callback](T ...source_args)
|
|
||||||
{
|
|
||||||
if (!std::holds_alternative<Nil>(callback->data)) {
|
|
||||||
auto result = (*callback)(*interpreter, { Number(source_args)... });
|
|
||||||
// We discard this since callback is running in another thread.
|
|
||||||
(void) result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,5 +1,4 @@
|
|||||||
#include <musique/interpreter/env.hh>
|
#include <musique/interpreter/env.hh>
|
||||||
#include <musique/interpreter/incoming_midi.hh>
|
|
||||||
#include <musique/interpreter/interpreter.hh>
|
#include <musique/interpreter/interpreter.hh>
|
||||||
#include <musique/try.hh>
|
#include <musique/try.hh>
|
||||||
|
|
||||||
@ -35,7 +34,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 +82,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 +123,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 +178,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 +190,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 ¶m : parameters) {
|
for (auto ¶m : 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,13 +216,13 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Error> Interpreter::play(Chord chord)
|
std::optional<Error> Interpreter::play(Chord chord)
|
||||||
{
|
{
|
||||||
Try(ensure_midi_connection_available(*this, Midi_Connection_Type::Output, "play"));
|
Try(ensure_midi_connection_available(*this, "play"));
|
||||||
auto &ctx = context_stack.back();
|
auto &ctx = context_stack.back();
|
||||||
|
|
||||||
if (chord.notes.size() == 0) {
|
if (chord.notes.size() == 0) {
|
||||||
@ -260,33 +259,16 @@ std::optional<Error> Interpreter::play(Chord chord)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Error> ensure_midi_connection_available(Interpreter &i, Midi_Connection_Type m, std::string_view operation_name)
|
std::optional<Error> ensure_midi_connection_available(Interpreter &i, std::string_view operation_name)
|
||||||
{
|
{
|
||||||
switch (m) {
|
if (i.midi_connection == nullptr || !i.midi_connection->supports_output()) {
|
||||||
break; case Midi_Connection_Type::Output:
|
return Error {
|
||||||
if (i.midi_connection == nullptr || !i.midi_connection->supports_output()) {
|
.details = errors::Operation_Requires_Midi_Connection {
|
||||||
return Error {
|
.is_input = false,
|
||||||
.details = errors::Operation_Requires_Midi_Connection {
|
.name = std::string(operation_name),
|
||||||
.is_input = false,
|
},
|
||||||
.name = std::string(operation_name),
|
.location = {}
|
||||||
},
|
};
|
||||||
.location = {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
break; case Midi_Connection_Type::Input:
|
|
||||||
if (i.midi_connection == nullptr || !i.midi_connection->supports_input()) {
|
|
||||||
return Error {
|
|
||||||
.details = errors::Operation_Requires_Midi_Connection {
|
|
||||||
.is_input = false,
|
|
||||||
.name = std::string(operation_name),
|
|
||||||
},
|
|
||||||
.location = {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
break; default:
|
|
||||||
unreachable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
@ -24,10 +25,6 @@ struct Interpreter
|
|||||||
|
|
||||||
std::function<std::optional<Error>(Interpreter&, Value)> default_action;
|
std::function<std::optional<Error>(Interpreter&, Value)> default_action;
|
||||||
|
|
||||||
struct Incoming_Midi_Callbacks;
|
|
||||||
std::unique_ptr<Incoming_Midi_Callbacks> callbacks;
|
|
||||||
void register_callbacks();
|
|
||||||
|
|
||||||
Interpreter();
|
Interpreter();
|
||||||
~Interpreter();
|
~Interpreter();
|
||||||
Interpreter(Interpreter const&) = delete;
|
Interpreter(Interpreter const&) = delete;
|
||||||
@ -56,7 +53,6 @@ struct Interpreter
|
|||||||
void register_builtin_operators();
|
void register_builtin_operators();
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Midi_Connection_Type { Output, Input };
|
std::optional<Error> ensure_midi_connection_available(Interpreter&, std::string_view operation_name);
|
||||||
std::optional<Error> ensure_midi_connection_available(Interpreter&, Midi_Connection_Type, std::string_view operation_name);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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>
|
||||||
}
|
}
|
||||||
@ -64,8 +66,6 @@ static T pop(std::span<char const*> &span)
|
|||||||
"usage: musique <options> [filename]\n"
|
"usage: musique <options> [filename]\n"
|
||||||
" where filename is path to file with Musique code that will be executed\n"
|
" where filename is path to file with Musique code that will be executed\n"
|
||||||
" where options are:\n"
|
" where options are:\n"
|
||||||
" -i,--input PORT\n"
|
|
||||||
" provides input port, a place where Musique receives MIDI messages\n"
|
|
||||||
" -o,--output PORT\n"
|
" -o,--output PORT\n"
|
||||||
" provides output port, a place where Musique produces MIDI messages\n"
|
" provides output port, a place where Musique produces MIDI messages\n"
|
||||||
" -l,--list\n"
|
" -l,--list\n"
|
||||||
@ -126,33 +126,22 @@ 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)
|
explicit Runner(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);
|
interpreter.midi_connection = &midi;
|
||||||
if (midi_go) {
|
|
||||||
interpreter.midi_connection = &midi;
|
|
||||||
}
|
|
||||||
if (output_port) {
|
if (output_port) {
|
||||||
std::cout << "Connected MIDI output to port " << *output_port << ". Ready to play!" << std::endl;
|
|
||||||
midi.connect_output(*output_port);
|
midi.connect_output(*output_port);
|
||||||
}
|
std::cout << "Connected MIDI output to port " << *output_port << ". Ready to play!" << std::endl;
|
||||||
if (input_port) {
|
} else {
|
||||||
std::cout << "Connected MIDI input to port " << *input_port << ". Ready for incoming messages!" << std::endl;
|
midi.connect_output();
|
||||||
midi.connect_input(*input_port);
|
std::cout << "Created new MIDI output port 'Musique'. Ready to play!" << std::endl;
|
||||||
}
|
|
||||||
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> {
|
||||||
@ -166,21 +155,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 +200,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,7 +291,6 @@ static std::optional<Error> Main(std::span<char const*> args)
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Arbitraly chosen for conviniance of the author
|
// Arbitraly chosen for conviniance of the author
|
||||||
std::optional<unsigned> input_port{};
|
|
||||||
std::optional<unsigned> output_port{};
|
std::optional<unsigned> output_port{};
|
||||||
|
|
||||||
std::vector<Run> runnables;
|
std::vector<Run> runnables;
|
||||||
@ -349,15 +327,6 @@ static std::optional<Error> Main(std::span<char const*> args)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg == "-i" || arg == "--input") {
|
|
||||||
if (args.empty()) {
|
|
||||||
std::cerr << "musique: error: option " << arg << " requires an argument" << std::endl;
|
|
||||||
std::exit(1);
|
|
||||||
}
|
|
||||||
input_port = pop<unsigned>(args);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg == "-o" || arg == "--output") {
|
if (arg == "-o" || arg == "--output") {
|
||||||
if (args.empty()) {
|
if (args.empty()) {
|
||||||
std::cerr << "musique: error: option " << arg << " requires an argument" << std::endl;
|
std::cerr << "musique: error: option " << arg << " requires an argument" << std::endl;
|
||||||
@ -375,7 +344,7 @@ static std::optional<Error> Main(std::span<char const*> args)
|
|||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Runner runner{input_port, output_port};
|
Runner runner{output_port};
|
||||||
|
|
||||||
for (auto const& [is_file, argument] : runnables) {
|
for (auto const& [is_file, argument] : runnables) {
|
||||||
if (!is_file) {
|
if (!is_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
|
||||||
@ -14,7 +13,6 @@ namespace midi
|
|||||||
virtual ~Connection() = default;
|
virtual ~Connection() = default;
|
||||||
|
|
||||||
virtual bool supports_output() const = 0;
|
virtual bool supports_output() const = 0;
|
||||||
virtual bool supports_input () const = 0;
|
|
||||||
|
|
||||||
virtual void send_note_on (uint8_t channel, uint8_t note_number, uint8_t velocity) = 0;
|
virtual void send_note_on (uint8_t channel, uint8_t note_number, uint8_t velocity) = 0;
|
||||||
virtual void send_note_off(uint8_t channel, uint8_t note_number, uint8_t velocity) = 0;
|
virtual void send_note_off(uint8_t channel, uint8_t note_number, uint8_t velocity) = 0;
|
||||||
@ -22,35 +20,28 @@ namespace midi
|
|||||||
virtual void send_controller_change(uint8_t channel, uint8_t controller_number, uint8_t value) = 0;
|
virtual void send_controller_change(uint8_t channel, uint8_t controller_number, uint8_t value) = 0;
|
||||||
|
|
||||||
void send_all_sounds_off(uint8_t channel);
|
void send_all_sounds_off(uint8_t channel);
|
||||||
|
|
||||||
std::function<void(uint8_t, uint8_t, uint8_t)> note_on_callback = nullptr;
|
|
||||||
std::function<void(uint8_t, uint8_t)> note_off_callback = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Rt_Midi : Connection
|
struct Rt_Midi : Connection
|
||||||
{
|
{
|
||||||
~Rt_Midi() override = default;
|
~Rt_Midi() override = default;
|
||||||
|
|
||||||
|
/// Connect with MIDI virtual port
|
||||||
|
void connect_output();
|
||||||
|
|
||||||
/// Connect with specific MIDI port for outputing MIDI messages
|
/// Connect with specific MIDI port for outputing MIDI messages
|
||||||
void connect_output(unsigned target);
|
void connect_output(unsigned target);
|
||||||
|
|
||||||
/// Connect with specific MIDI port for reading MIDI messages
|
|
||||||
void connect_input(unsigned target);
|
|
||||||
|
|
||||||
/// List available ports
|
/// List available ports
|
||||||
void list_ports(std::ostream &out) const;
|
void list_ports(std::ostream &out) const;
|
||||||
|
|
||||||
bool supports_output() const override;
|
bool supports_output() const override;
|
||||||
bool supports_input () const override;
|
|
||||||
|
|
||||||
void send_note_on (uint8_t channel, uint8_t note_number, uint8_t velocity) override;
|
void send_note_on (uint8_t channel, uint8_t note_number, uint8_t velocity) override;
|
||||||
void send_note_off(uint8_t channel, uint8_t note_number, uint8_t velocity) override;
|
void send_note_off(uint8_t channel, uint8_t note_number, uint8_t velocity) override;
|
||||||
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<RtMidiOut> output;
|
std::optional<RtMidiOut> output;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,22 +34,22 @@ try {
|
|||||||
std::exit(33);
|
std::exit(33);
|
||||||
}
|
}
|
||||||
|
|
||||||
void midi::Rt_Midi::connect_output(unsigned target)
|
void midi::Rt_Midi::connect_output()
|
||||||
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");
|
||||||
} 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;
|
||||||
std::exit(33);
|
std::exit(33);
|
||||||
}
|
}
|
||||||
|
|
||||||
void midi::Rt_Midi::connect_input(unsigned target)
|
void midi::Rt_Midi::connect_output(unsigned target)
|
||||||
try {
|
try {
|
||||||
assert(not input.has_value(), "Reconeccting is not supported yet");
|
ensure(not output.has_value(), "Reconeccting is not supported yet");
|
||||||
input.emplace();
|
output.emplace();
|
||||||
input->openPort(target, "Musique input port");
|
output->openPort(target);
|
||||||
} 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;
|
||||||
@ -61,11 +61,6 @@ bool midi::Rt_Midi::supports_output() const
|
|||||||
return bool(output);
|
return bool(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool midi::Rt_Midi::supports_input() const
|
|
||||||
{
|
|
||||||
return bool(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<std::size_t N>
|
template<std::size_t N>
|
||||||
inline void send_message(RtMidiOut &out, std::array<std::uint8_t, N> message)
|
inline void send_message(RtMidiOut &out, std::array<std::uint8_t, N> message)
|
||||||
try {
|
try {
|
||||||
@ -104,6 +99,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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
@ -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;
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
@ -17,7 +17,7 @@ Chord::Chord(std::vector<Note> &¬es)
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,26 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
#include <compare>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
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 = std::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 +46,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 +122,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 +159,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 +169,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; }
|
||||||
|
24
scripts/build.mk
Normal file
24
scripts/build.mk
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
Release_Obj=$(addprefix bin/$(os)/,$(Obj))
|
||||||
|
|
||||||
|
bin/$(os)/bestline.o: lib/bestline/bestline.c lib/bestline/bestline.h
|
||||||
|
@echo "CC $@"
|
||||||
|
@$(CC) $< -c -O3 -o $@
|
||||||
|
|
||||||
|
bin/$(os)/%.o: musique/%.cc
|
||||||
|
@echo "CXX $@"
|
||||||
|
@$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $< -c
|
||||||
|
|
||||||
|
bin/$(os)/$(Target): $(Release_Obj) bin/$(os)/main.o bin/$(os)/rtmidi.o $(Bestline)
|
||||||
|
@echo "CXX $@"
|
||||||
|
@$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $(Release_Obj) bin/$(os)/rtmidi.o $(Bestline) $(LDFLAGS) $(LDLIBS)
|
||||||
|
|
||||||
|
Debug_Obj=$(addprefix bin/$(os)/debug/,$(Obj))
|
||||||
|
|
||||||
|
bin/$(os)/debug/$(Target): $(Debug_Obj) bin/$(os)/debug/main.o bin/$(os)/rtmidi.o $(Bestline)
|
||||||
|
@echo "CXX $@"
|
||||||
|
@$(CXX) $(CXXFLAGS) $(DEBUG_FLAGS) $(CPPFLAGS) -o $@ $(Debug_Obj) bin/$(os)/rtmidi.o $(Bestline) $(LDFLAGS) $(LDLIBS)
|
||||||
|
|
||||||
|
bin/$(os)/debug/%.o: musique/%.cc
|
||||||
|
@echo "CXX $@"
|
||||||
|
@$(CXX) $(CXXFLAGS) $(DEBUG_FLAGS) $(CPPFLAGS) -o $@ $< -c
|
||||||
|
|
@ -1,11 +0,0 @@
|
|||||||
Debug_Obj=$(addprefix bin/debug/,$(Obj))
|
|
||||||
|
|
||||||
debug: bin/debug/musique
|
|
||||||
|
|
||||||
bin/debug/musique: $(Debug_Obj) bin/debug/main.o bin/bestline.o
|
|
||||||
@echo "CXX $@"
|
|
||||||
@$(CXX) $(CXXFLAGS) $(DEBUG_FLAGS) $(CPPFLAGS) -o $@ $(Debug_Obj) bin/bestline.o $(LDFLAGS) $(LDLIBS)
|
|
||||||
|
|
||||||
bin/debug/%.o: musique/%.cc
|
|
||||||
@echo "CXX $@"
|
|
||||||
@$(CXX) $(CXXFLAGS) $(DEBUG_FLAGS) $(CPPFLAGS) -o $@ $< -c
|
|
6
scripts/linux.mk
Normal file
6
scripts/linux.mk
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
CC=gcc
|
||||||
|
CXX=g++
|
||||||
|
CPPFLAGS:=$(CPPFLAGS) -D __LINUX_ALSA__
|
||||||
|
LDLIBS:=-lasound $(LDLIBS) -static-libgcc -static-libstdc++
|
||||||
|
Bestline=bin/$(os)/bestline.o
|
||||||
|
Target=musique
|
7
scripts/macos.mk
Normal file
7
scripts/macos.mk
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
CC=clang
|
||||||
|
CXX=clang++
|
||||||
|
CPPFLAGS:=$(CPPFLAGS) -D __MACOSX_CORE__
|
||||||
|
LDLIBS:=-framework CoreMIDI -framework CoreAudio -framework CoreFoundation $(LDLIBS)
|
||||||
|
Release_Obj=$(addprefix bin/,$(Obj))
|
||||||
|
Bestline=bin/$(os)/bestline.o
|
||||||
|
Target=musique
|
@ -16,10 +16,10 @@ fi
|
|||||||
|
|
||||||
mkdir -p "$Target"
|
mkdir -p "$Target"
|
||||||
|
|
||||||
make clean && make bin/musique
|
make clean && make os=linux
|
||||||
cp bin/musique "$Target"/
|
cp bin/musique "$Target"/
|
||||||
|
|
||||||
make clean && make bin/musique os=windows
|
make clean && make os=windows
|
||||||
cp bin/musique.exe "$Target"/
|
cp bin/musique.exe "$Target"/
|
||||||
|
|
||||||
echo "Copy examples, license and documentation"
|
echo "Copy examples, license and documentation"
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
ifneq ($(os),windows)
|
|
||||||
Release_Obj=$(addprefix bin/,$(Obj))
|
|
||||||
|
|
||||||
bin/bestline.o: lib/bestline/bestline.c lib/bestline/bestline.h
|
|
||||||
@echo "CC $@"
|
|
||||||
@$(CC) $< -c -O3 -o $@
|
|
||||||
|
|
||||||
bin/%.o: musique/%.cc
|
|
||||||
@echo "CXX $@"
|
|
||||||
@$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $< -c
|
|
||||||
|
|
||||||
bin/musique: $(Release_Obj) bin/main.o bin/rtmidi.o bin/bestline.o
|
|
||||||
@echo "CXX $@"
|
|
||||||
@$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $(Release_Obj) bin/rtmidi.o bin/bestline.o $(LDFLAGS) $(LDLIBS)
|
|
||||||
endif
|
|
@ -1,11 +1,5 @@
|
|||||||
ifeq ($(os),windows)
|
CC=i686-w64-mingw32-gcc
|
||||||
Release_Obj=$(addprefix bin/,$(Obj))
|
CXX=i686-w64-mingw32-g++
|
||||||
|
CPPFLAGS:=$(CPPFLAGS) -D__WINDOWS_MM__
|
||||||
bin/%.o: musique/%.cc
|
LDLIBS:=-lwinmm $(LDLIBS) -static-libgcc -static-libstdc++
|
||||||
@echo "CXX $@"
|
Target=musique.exe
|
||||||
@$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $< -c
|
|
||||||
|
|
||||||
bin/musique: $(Release_Obj) bin/main.o bin/rtmidi.o
|
|
||||||
@echo "CXX $@"
|
|
||||||
@$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $(Release_Obj) bin/rtmidi.o $(LDFLAGS) $(LDLIBS)
|
|
||||||
endif
|
|
||||||
|
Loading…
Reference in New Issue
Block a user