diff --git a/.gitignore b/.gitignore index 63c23f6..9455005 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ doc/source/api __pycache__ drafts.cc .cache +release_* +*.zip diff --git a/Makefile b/Makefile index 40e9964..e84b689 100644 --- a/Makefile +++ b/Makefile @@ -37,6 +37,11 @@ doc-open: doc clean: rm -rf bin coverage -.PHONY: clean doc doc-open all test unit-tests +release: bin/musique + scripts/release + + + +.PHONY: clean doc doc-open all test unit-tests release $(shell mkdir -p bin/debug/tests) diff --git a/README.md b/README.md index 45df299..192720f 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ $ make bin/musique Skopiuj plik [etc/editor/musique.vim](etc/editor/musique.vim) do folderu `syntax` wewnątrz twojej konfiguracji Vima (Neovima). Np: ```console -$ cp etc/editor/musique.vim ~/.config/nvim/syntax/ +$ cp editor/musique.vim ~/.config/nvim/syntax/ ``` Następnie musisz dodać ustawienie typu pliku na podstawie rozszerzenia wewnątrz twojej konfiguracji: diff --git a/doc/cheatsheet.txt b/doc/cheatsheet.txt deleted file mode 100644 index e3fe05b..0000000 --- a/doc/cheatsheet.txt +++ /dev/null @@ -1,119 +0,0 @@ ---------------------------------------------------------------------------- -Składnia ---------------------------------------------------------------------------- - -Zapis nut (dostępna notacja zachodnia) - c - d - -Ustawianie oktawy - c 8 - -Ustawianie oktawy i długości - c 8 (1/4) c w oktawie 8 grane przez 1/4 długości pełnej nuty - -Ustawianie oktawy i długości, a następnie modyfikacja - (c 8 fn) 4 zmienia oktawę na 4 - (c 8 fn) 4 qn zmienia oktawę na 4 i długość na 1/4 - -Stworzenie akordu - c123 == chord[c; c#; d; d#] - -Nutę możemy modifikować poprzez podwyższanie o półton (# lub s), lub obniżanie o półton (b lub f) - c## == css == d == dff == dbb - -Arytmetyka - (aktualnie brak wsparcia dla precedensji operatorów) - 3 * (4 * 12 - 8) - -Przesuwanie dźwięków o półtony - c + 1 == c# - c - 1 == b - c12 + 1 == c#12 - -Wywołanie funkcji - foo 1 2 3 - -Kilka wywołań obok siebie - foo 1; bar 2 - -Stworzenie zmiennej - var variable = 20 - -Blok - Jako tablica - [1;2;3] - - Jako odroczona ewaluacja - [say 42] - - Jako funkcja anonimowa jednoparametrowa - [i | i * i ] - - Poniższe zapisy są równoważne - [ say 42 ] - [| say 42 ] - ---------------------------------------------------------------------------- -Dostępne stałe ---------------------------------------------------------------------------- - -fn 1 -dfn 3/2 -hn 1/2 -dhn 3/4 -ddhn 7/8 -qn 1/4 -dqn 3/8 -ddqn 7/16 -en 1/8 -den 3/16 -dden 7/32 -sn 1/16 -dsn 3/32 -tn 1/32 -dtn 3/64 - ---------------------------------------------------------------------------- -Dostępne funkcje ---------------------------------------------------------------------------- - -flat : ...items : any -> array - tworzy tablicę z dostarczonych elementów, rozsypując elementy indeksowalne - przykład: - flat 1 2 3 tworzy tablicę 1, 2, 3 - flat [1;2] 2 3 tworzy tablicę 1, 2, 2, 3 - flat [[1;2]; [3;4]] 5 6 tworzy tablicę [1;2], [3;4], 5, 6 - - -if : condition: bool, block: block -> (eval block) - wywołuje block jeśli condition jest: - dla b: bool jest b == true - dla n: number jest n != 0 - dla nil jest false - dla typu music, block, intrinsic, symbol zawsze prawdziwe - -if : condition: Bool, then: Block, else: Block -> (eval then or eval else) - jeśli condition jest prawdziwe wg zasad wyżej wywołaj then - w innym przypadku wywołaj else - -len : array -> number - zwraca ilość elementów w tablicy - Przykład: - len (flat 5 6 7) == 3 - -len : block -> number - zwraca ilość elementów (liczbę indeksowalnych pozycji) wewnątrz bloku - Przykład: - len [1;2;3] == 3 - len [foo;bar] == 2 - len [foo bar] == 1 - -play : music... -> nil - odtwarza sekwencyjnie otrzymane wartości muzyczne - -chord : (collection | music)... -> music - tworzy akord z przekazanych wartości muzycznych - -par : x: music -> xs: playable... -> nil - odtwarza dźwięk x równolegle z wszystkimi dźwiękami xs diff --git a/doc/overview.md b/doc/overview.md new file mode 100644 index 0000000..172a580 --- /dev/null +++ b/doc/overview.md @@ -0,0 +1,309 @@ +## Korzystanie z interpretera w systemie GNU/Linux + +Uruchamiać interpreter można na dwa sposoby: + +- w trybie interaktywnym poprzez polecenie `musique` +- w trybie wsadowym poprzez podanie nazw plików do programu musique `musique utwór_a.mq utwór_b.mq` + +Pliki zawierające kod Musique mają według konwencji rozszerzenie `mq`. + +Tryb interaktywny jak i wsadowy nie różnią się swoimi możliwościami. Można połączyć ich działanie poprzez podanie parametru `--repl` w trakcie uruchamiania interpretera. + +Aby opuścić interpreter należy użyć skrótu klawiszowego `CTRL-D`, `CTRL-C` lub polecenia `:quit`. + +Interpreter posiada historię poprzednich poleceń, którą można przechodzić przy pomocy strzałek. + +### Przykłady uruchamiania i korzystania z interpretera + +Uruchom wyłącznie tryb interaktywny: + +``` +$ musique +> play c e g +``` + +Uruchom plik `examples/ode-to-joy.mq`, który odgrywa "Odę do radości": + +``` +$ musique examples/ode-to-joy.mq +``` + +Uruchom plik `examples/factorial.mq`, który wypisuje kolejne wartości silni, ale też tryb interaktywny, w którym wykorzystamy definicje z pliku: + +``` +$ musique examples/factorial.mq +> factorial 5 +120 +``` + +## Podstawowe koncepty + +### Komentarze + +Komentarze pozwalają umieszczać opisy i _komentarze_ składające się z języka naturalnego wewnątrz kodu Musique. Są dwa rodzaje komentarzy: liniowe oraz wieloliniowe. + +Komentarze liniowe rozpoczynają się od znaków `--` lub `#!` i kończą się wraz z zakończeniem linii. + +Komentarze wieloliniowe rozpoczynają się od co najmniej trzech znaków `-` i kończą się na co najmniej 3 znakach `-`. Pozwala to na następujące kształty komentarzy: + +``` +-- komentarz do końca linii +say 42; -- komentarz do końca linii, wyrażenia przed nim są wykonywane + +--- komentarz wieloliniowy +say 43 <- to nie zostanie wykonane +--- + +say 44; + +---------------------------------------------- +A tutaj mamy wieloliniowy komentarz, który +ma ładne obramowania +---------------------------------------------- +``` + +### Wartości w języku + +Musique jest językiem dynamicznie typowanym - zmienne nie mają typu, wyłącznie wartości je mają. + +__Typ__ jest to rodzaj wartości, determinujący w jaki sposób można je transformować, wyświetlać, grać czy w inny sposób przetwarzać. + +Musique posiada kilka typów: _nil_, _bool_, _number_, _symbol_, _intrinsic_, _block_, _array_, _music_. Typ _nil_ ma pojedyńczą wartość, __nil__, która istnieje dla odróżnienia wartości od wszystkich innych wartości wartości oraz reprezentowania braku użytecznej wartości. + +Typ logiczny _bool_, posiada dwie wartości: __true__ i __false__, reprezentujące odpowiednio prawdę i fałsz. __false__ nie jest jedyną metodą reprezentowania wartości fałszywej; w kontekście _warunku_ __0__, __false__, __nil__ są widziane jako fałszywe, stąd nazywane są wartościami fałszywymi (_false values_). + +Typ liczbowy _number_ reprezentuje liczby wymierne, których licznik i mianownik mogą być 64-bitowymi liczbami całkowitymi. Sprawia to, że możliwym jest precyzyjne definiowanie ułamków oznaczających długość dżwięku jak `1/3` lub `3`. + +Typ _symbol_ reprezentuje identyfikatory jak i stałe nazwane wewnątrz języka. + +Typ funkcji wbudowanych _intrinsic_ jest używany do przechowywania funkcji napisanych wewnątrz interpretera, jak `play`, `note_on`, `par`, itp. + +Typ blokowy _block_ reprezentuje kolekcję wartości, opcjonalnie sparemetryzowaną. Inaczej mówiąc blok, reprezentuje zarówno funkcje jak i tablice w Musique. Co ważne ewaluacja wnętrza bloku jest _leniwe_ - dopóki blok nie zostanie wywołany lub jego pole zindekowane, nie zostanie jego wnętrze wykonane. Blok posiadający parametry nie może zostać zindeksowany, gdyż jego wnętrze ulegnie zmianom w zależności od podanych parametrów. + +Typ tablicowy _array_ reprezentuje kolekcję wartości, która w przeciwieństwie do bloków nie jest leniwa - w momencie tworzenia wszystkie wartości są już znane. Stosowana jest w celu przechowywania wyników transformacji bloków, gdyż transformowanie bloku oblicza jego wartości. + +Typ muzyczny _music_ reprezentuje akord (w szczególności składający się z jednego dźwięku) w skali oferowanej przez MIDI. Najmniejszą jednostką jest półton, pojedyńczy dźwięk jest reprezentowany jako trójka (baza, opcjonalna oktawa, opcjonalna długość). Dźwięk w momencie grania może dostać domyślną wartość długości czy oktawy. Można także własnoręcznie ustawić oktawę i długość poprzez wywołanie dźwięku `c 4 (1/4)`. Jeden akord może zawierać wiele dźwięków o różnych długościach grania. + +Funkcja wbudowana `typeof` wypisuje symbol reprezentujący dany typ. + +### Odśmiecanie pamięci (garbage collection) + +Język korzysta z automatycznego mechanizmu zarzadzania pamięcią. Sprawia to, że osoba korzystająca z pamięci nie musi przejmować się alokacją pamięci. Aktualny sposób zarzadzania pamięcią opiera się na wbudowanym w bibliotekę standardową języka C++ mechanizmu zliczania referencji. Nie jest on widziany jako ostateczny, a jako prototypowy. Może prowadzić to do nieoczekiwanego zachowania interpretera, objawiającego się wolnym działaniem i nadmiernym użytkowaniem pamięci. + +## Język + +Ta sekcja omawia składnię ("wygląd") oraz semantykę ("zachowanie") języka Musique. Jest to omówienie jakie programy są prawidłowe i co one znaczą. + +### Składnia języka oraz literały + +Kod języka Musique jest zakodowany jako UTF-8. + +Musique jest językiem "free-form" - białe znaki są używane tylko do odróżnienia kolejnych słów i znaków języka, ale nie determinują jego znaczenia. Musique rozpoznaje wyłącznie białe znaki ASCII jako separatory - znaki jak niepodzielna spacja nie są rozpatrywane jako separatory. + +Nazwy (identifikatory, _identifiers_) są dowolną sekwencją liter (w tym litery polskie; widzianych przez Unicode jako litery), liczb oraz znaków: `_'#$@`. Nie mogą rozpoczynać się cyfrą, nie mogą być literałem muzycznym lub zarezerwowanym identyfikatorem. + +Następujące identyfikatory są zarezerwowane: `and` oraz `or`. + +Musique jest językiem zwracającym uwagę na wysokość liter: + +- `and` jest zarezerwowane, ale `And` nie jest. +- `c` jest literałem muzycznym, `C` może być identyfikatorem + +#### Stałe liczbowe + +Stała liczbowa (literał liczbowy) jest to ciąg cyfr, z potencjalnym ciągiem cyfr oznaczający część dziesiętną poprzedzony kropką. + +Przykłady stałych liczbowych: + +- `10.25` reprezentuje wartość `10` i `1/4`, +- `0` reprezentuje wartość `0`, +- `0.0` reprezentuje wartość `0`. + +Musique posiada tylko jeden sposób sposób reprezentacji liczb dlatego `0` jest tym samym co `0.0`. + +Musique __nie__ posiada stałych innych niż dziesiętne, także szesnastkowych (heksadecymalnych) czy ósemkowych. + +#### Stałe muzyczne + +Stałe muzyczne (literały muzyczne) jest to nazwa dźwięku z opcjonalnym przyrostkiem będącym ciągiem cyfr. + + +| Nazwa dźwięku | Wartość bazowa | Numer MIDI w oktawie 4 | +| :-: | :-: | :-: | +| __c__ | 0 | 60 | +| __d__ | 2 | 62 | +| __e__ | 4 | 64 | +| __f__ | 5 | 65 | +| __g__ | 7 | 67 | +| __a__ | 9 | 69 | +| __b__ | 11 | 71 | + +Numer dźwięku może być modyfikowany o półton poprzez dodanie po nazwie dźwięku (co najmniej jednego): + +- `#` lub `s` zwiększa o jeden półton numer dźwięku +- `b` lub `f` zmniejsza o jeden półton numer dźwięku + +Numer dźwięku może być ujemny - może sprawić, że dźwięk "spadnie" do niższej oktawy. + +Po nazwie dźwięku można przy pomocy liczb określić akord. Każda kolejna cyfra oznacza kolejny dźwięk w ramach akordu, liczony bezwględnie od dźwięku bazowego jako przesunięcie o daną liczbę półtonów. `c47` określa akord składający się z dźwięków `c`, `c+4`, `c+7` - akord C-dur. + +### Zmienne + +Zmienne są to miejsca przechowywania wartości. + +Nazwa, określona przez symbol, reprezentuje odwołanie do stworzonej wcześniej zmiennej. Nie można używać zmiennych, które nie zostały wcześniej _zadeklarowane_. + +Deklaracja zmiennych sprawia, że zmienna istnieje. Równocześnie z jej tworzeniem, można przypisać jej wartość. + +``` +x := 10; +y := 20; + +say x y; -- wypisuje 10 20 + +t := x; +x = y; +y = t; + +say x y; -- wypisuje 20 10 +``` + +Zmienne mają zasięg leksykalny - mają dostęp tylko do zmiennych z kontekstu ich tworzenia, a nie np. z miejsca gdzie blok został wywołany. + +### Wyrażenia + +Musique wspiera następujące typy wyrażeń: + +- sekwencję wyrażeń jak `foo; bar; foo hello` +- wyrażenia blokowe jak `[10;20;30]` +- wywołania funkcji jak `call_me maybe` +- binarne operacje jak: `10 + 20` + +### Bloki + +Są sekwencją wyrażeń. Każde kolejne wyrażenie oddzielone jest znakiem średnika `;`. Dla wygody, można stosować więcej niż jeden średnik do oddzielania wyrażeń (przypadkowe wciśnięcia nie są karane). Każy blok otoczony jest parą nawiasów kwadratowych `[]`, które determinują zasięg widoczności zadeklarowanych w nim zmiennych, w tym parametrów. + +Blok może rozpoczynać się sekcją parametrów, która może zawierać wyłącznie symbole, a kończy się znakiem `|`. W momencie wywoływania bloku, każdemu parametrowi odpowiadać powinień argument, który nada parametrowi wartość. Literały muzyczne jak `c` __nie mogą__ zostać użyte jako parametry bloku. + +Przykłady zastosowania bloków: + +``` +-- Blok, który nie przyjmuje żadnych parametrów i nie posiada żadnych wartości +[]; + +-- Blok, który nie przyjmuje żadnych parametrów, ale posiada wartość +x := [10]; +say x.0; -- wypisuje 10 +say (call x); -- wypisuje 10 + +-- Blok, który nie przyjmuje żadnych parametrów, ale posiada wiele wartości +x := [10; 20; 30]; +say x.0; -- wypisuje 10 +say (call x); -- wypisuje 30 + +-- Blok, który przyjmuje parametry i zwraca ich sumę +add := [x y | x + y ]; +say (add 101 22); -- wypisuje 123 + +-- Blok, który odwołuje się do samego siebie +count_down := [n| say n; if (n >= 0) [ count_down (n-1) ] ]; +count_down 10; -- wypisuje kolejne wartości od 10 do 0 włącznie +``` + +### Operacje binarne + +#### Operacje logiczne + +Musique wspera sume logiczną `or` oraz iloczyn logiczny `and`. Ponadto dostępne są operatory równości `==` oraz `!=`. + +#### Operacje liczbowe + +Musique wspiera standardowe operacje arytmetyczne (`+`, `-`, `*`, `/` oraz potęga '**') oraz porównania (`<`, `<=`, `==`, `!=`, `>=`, `>`). + +#### Operacje na wartościach muzycznych + +Wspierane są operatory porównania `<`, `<=`, `==`, `!=`, `>=`, `>`. Porównywać można wartości tylko o tym samym stopniu wypełnienia informacji - jeśli jedna z wartości nie ma wyspecjalizowanej podwartości jak oktawy czy długości nie można ich nawzajem porównać. + +- __Dodawanie półtonów__ `a + b` gdzie `a` jest wartością muzyczną (liczbową), a `b` jest wartością liczbową (muzyczną). +- __Odejmowanie półtonów__ `a - b` gdzie `a` jest wartością muzyczną (liczbową), a `b` jest wartością liczbową (muzyczną). + +Wartości muzyczne i liczbowe można zestawiać koło siebie: + +- `c 1 2` tworzy `c` w oktawie 1 o długości 2 +- `c c` tworzy sekwencję dźwięków `[c;c]` +- `c e g` tworzy sekwencję dźwięków `[c;e;g]` +- `c 4 e 5 g 4` tworzy sekwencję dźwięków: c w oktawie 4, e w oktawie 5 i g w oktawie 4 + +## Funkcje wbudowane + +### Funkcje muzyczne + +`par A B...` pozwala na zagranie wartości muzycznej `A` przez całą długość odtwarzania częsci `B`. + +`sim a b` gra równocześnie część `A` i `B`. + +`play A...` odtwarza sekwenencyjne kolejne grupy dźwięków. + +Gdy funkcje `par` lub `play` dostaną tablicę wartości muzycznych tablice zostaną odtworzone sekwencyjnie. Inaczej mówiąc `play a [c;e;d] b ≡ play a c e d b`. + +#### Modyfikacja kontekstu + +`bpm A` ustawia długość `A` odpowiadającą ćwierćnucie w uderzeniach na minutę. + +`len A` ustawia domyślną długość nuty jako `A` + +`oct A` ustawia domyślną oktawę + +### Funkcje sterowania przepływem + +`if ` wykonuje blok `then` jeśli `condition` jest rozpatrywane jako prawdziwe. Jeśli blok `` zostanie dostarczony, to zostanie wywołany dla warunku fałszywego. + +``` +if true [say 42] [say 10] -- wyświetla 42 +if false [say 42] [say 10] -- wyświetla 10 +say (if false [42] [10]) -- wyświetla 10 +say (if false [42]) -- wyświetla nil +``` + +### Funkcje matematyczne + +`max` znajduje maksimum z podanych wartości, a `min` znajdume minimum. W momencie podania tablic, znajduje maksimum / minumum z danej tablicy i pozostałych wartości przekazanych do funkcji. + +__Wartości muzyczne, logiczne i liczbowe__ są widziane jako porównywalne tylko gdy mają ten sam typ: `c < d`, `0 < 1`, `false < true` itp. + +### Funkcje tablicowe + +`permute` generuje kolejną permutację tablicy. + +`reverse` zwraca odwróconą tablicę. + +`sort` zwraca tablicę z posortowanymi elementami + +`partition` dzieli podane wartości wg predykatu, tak, że zwraca dwuelementową tablicę, w którym pierwszym elementem jest tablica wartości dla których predykat zwrócił prawdę, a drugim elementem jest tablica wartości dla których predykat zwrócił fałsz. + +``` +say (partition [i | i > 5] 1 2 3 4 5 [1; 2; 3; 8] 9 0) +-- wypisuje: [[8; 9]; [1; 2; 3; 4; 5; 1; 2; 3; 0]] +``` + +`shuffle` zwraca tablicę z losową kolejnością elementów. + +`flat` spłaszcza przekazane tablice (o jeden rząd) + +``` +say (flat 1 2 3); +-- wypisuje [1;2;3] +say (flat [1;2;3]); +-- wypisuje [1;2;3] +say (flat [1;2;3] 4 [5;6]); +-- wypisuje [1;2;3;4;5;6] +``` + +### Funkcje MIDI + +`note_on ` wysyła komunikat _Note on_. + +`note_off ` wysyła komunikat _Note off_. + +`pgmchange` (znane także jako `instrument`) wysyła komunikat _Program Change_. + diff --git a/editor/musique.vim b/editor/musique.vim index 318e517..c53d7d2 100644 --- a/editor/musique.vim +++ b/editor/musique.vim @@ -8,8 +8,8 @@ if exists("b:current_syntax") finish endif -syn keyword musiqueVariableDeclaration var -syn keyword musiqueOperators * + - / < <= == >= > != . +syn keyword musiqueVariableDeclaration := +syn keyword musiqueOperators * + - / < <= == >= > != . ** and or syn match musiqueParameterSplitter display "|" syn match musiqueExpressionDelimiter display ";" @@ -18,7 +18,7 @@ syn match musiqueInteger display "[0-9][0-9_]*" syn keyword musiqueConstant true false nil -syn keyword musiqueDefaultBuiltins if len play permute par shuffle chord bpm oct note_on note_off flat update +syn keyword musiqueDefaultBuiltins bpm call ceil chord down flat floor fold for hash if incoming instrument len max min mix note_off note_on nprimes oct par partition permute pgmchange play program_change range reverse rotate round shuffle sim sort try typeof uniq unique up update syn keyword musiqueLinuxBuiltins say syn match musiqueComment "--.*$" diff --git a/examples/for-elise.mq b/examples/for-elise.mq index b8e2f4a..4527a60 100644 --- a/examples/for-elise.mq +++ b/examples/for-elise.mq @@ -1,9 +1,10 @@ ---------------------------------------------- "Für Elise in A Minor" by Ludwig van Beethoven +WIP implemntation ---------------------------------------------- oct 5; bpm 72; len (1/16); -subsection1 := [ n | +subsection1 := [ sim (a 4 en) (a 2 e 3 a 3); play [oct 4; c e a]; @@ -26,7 +27,7 @@ section1 := [ n | play (e d#); play (e d# e b 4 d c); - subsection1 0; + call subsection1; if (n == 1) [ sim (a 4 qn) (a 2 e 3 a 3); ] @@ -51,7 +52,7 @@ section2 := [ n | play (d# e d# e d#); play (e d# e b 4 d c); - subsection1 0; + call subsection1; if (n == 1) [ sim (a 4 en) (a 2 e 3 a 3) diff --git a/scripts/definitions b/scripts/definitions new file mode 100755 index 0000000..b5959cc --- /dev/null +++ b/scripts/definitions @@ -0,0 +1,3 @@ +#!/bin/sh + +awk -F'"' '/global\.force_define\(/ { print $2 }' $1 | sort | uniq diff --git a/scripts/release b/scripts/release new file mode 100755 index 0000000..33f06de --- /dev/null +++ b/scripts/release @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +set -e -o pipefail + +Suffix="$(date +"%Y-%m-%d")" +Target="release_$Suffix" + + +if [ -d "$Target" ]; then + rm -rf "$Target" +fi + +mkdir -p "$Target" + +if ! [ -f bin/musique ]; then + make bin/musique +fi + +echo "Copy bin/musique, examples, license and documentation" + +cp bin/musique "$Target"/ +cp LICENSE "$Target"/LICENSE +cp -r examples "$Target"/examples + +lowdown -s doc/overview.md -m "title:Omówienie języka Musique" -o "$Target"/overview.html +lowdown -s doc/functions.md -m "title:Lista funkcji języka Musique" -o "$Target"/functions.html + +echo "Copy source code" + +git clone --quiet --depth=1 "$(git remote -v | awk '{ print $2 }' | head -n1)" "$Target"/source_code +rm -rf "$Target"/source_code/.git + +echo "Boundle it all up" + +zip -q -r "musique_$Suffix.zip" "$Target"/* diff --git a/src/builtin_functions.cc b/src/builtin_functions.cc index e804453..acc90f9 100644 --- a/src/builtin_functions.cc +++ b/src/builtin_functions.cc @@ -156,7 +156,7 @@ enum class Range_Direction { Up, Down }; /// Create range according to direction and specification, similar to python template -Result builtin_range(Interpreter&, std::vector args) +static Result builtin_range(Interpreter&, std::vector args) { using N = Shape; using NN = Shape; @@ -920,11 +920,33 @@ static Result builtin_mix(Interpreter &i, std::vector args) return Value::from(std::move(result)); } +/// Call operator. Calls first argument with remaining arguments +static Result builtin_call(Interpreter &i, std::vector args) +{ + auto const guard = Guard<1> { + .name = "call", + .possibilities = { + "(function, ...args) -> any" + } + }; + + if (args.size() == 0) { + return guard.yield_error(); + } + + auto callable = args.front(); + Try(guard(is_callable, callable)); + args.erase(args.begin()); + + return callable(i, std::move(args)); +} + void Interpreter::register_builtin_functions() { auto &global = *Env::global; global.force_define("bpm", ctx_read_write_property<&Context::bpm>); + global.force_define("call", builtin_call); global.force_define("ceil", apply_numeric_transform<&Number::ceil>); global.force_define("chord", builtin_chord); global.force_define("down", builtin_range);