Builtin function documentation generates from implementation
This commit is contained in:
parent
b51088a9f0
commit
bda1e503c7
@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Printing version number on non-quiet launch, or when provided `--version` or `:version`
|
||||||
|
- Builtin function documentation generation from C++ Musique implementation source code
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Release builder, since it's separate part of the project
|
||||||
|
|
||||||
## [0.3.1]
|
## [0.3.1]
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
3
Makefile
3
Makefile
@ -44,6 +44,9 @@ doc/musique-vs-languages-cheatsheet.html: doc/musique-vs-languages-cheatsheet.te
|
|||||||
doc/wprowadzenie.html: doc/wprowadzenie.md
|
doc/wprowadzenie.html: doc/wprowadzenie.md
|
||||||
pandoc -o $@ $< -s --toc
|
pandoc -o $@ $< -s --toc
|
||||||
|
|
||||||
|
doc/functions.html: musique/interpreter/builtin_functions.cc scripts/document-builtin.py
|
||||||
|
scripts/document-builtin.py -o $@ $<
|
||||||
|
|
||||||
.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/$(os)/,$(shell find musique/* -type d)))
|
$(shell mkdir -p $(subst musique/,bin/$(os)/,$(shell find musique/* -type d)))
|
||||||
|
115
doc/functions.md
115
doc/functions.md
@ -1,115 +0,0 @@
|
|||||||
# Lista wbudowanych funkcji języka Musique
|
|
||||||
|
|
||||||
* `bmp value` – zmienia wartość BMP z domyślnej na `value`;
|
|
||||||
-`value` musi być liczbą całkowitą, domyślnie `120`;
|
|
||||||
|
|
||||||
* `call function args` – funkcja wywołująca funkcję `function`, która przekazuje `function` `args` jako argumenty wywoływanej fukncji;
|
|
||||||
|
|
||||||
* `ceil value` – operacja podobna do matematycznej funkcji podłogi (zaokrąglenie liczby do pierwszej liczby całkowitej mniejszej lub równej tej liczbie);
|
|
||||||
- `value` – musi być to wartość o typie Number lub tablica takich wartości;
|
|
||||||
|
|
||||||
* `chord (notes)` – konstruuje akord z `notes`:
|
|
||||||
- `notes` – `notes` definiowane są następująco: `(<litera_nuty> <numer_oktawy> <czas_trwania>)`:
|
|
||||||
- np. `(c 4 1)` – dźwięk C w 4 oktawie, o długości całej nuty;
|
|
||||||
|
|
||||||
* `down value` – sekwencyjnie zwraca liczby całkowite, począwszy od `value` do 0:
|
|
||||||
- `value` – musi być liczbą całkowitą;
|
|
||||||
|
|
||||||
* `flat args` – łączy `args` w tablicę bez zagnieżdżeń (tzn. "odpakowuje" zawartość zagnieżdżonych tablic i zawiera je w pojedyńczej tabeli):
|
|
||||||
- `args` - tablica, w tym tablica z zagnieżdżeniami;
|
|
||||||
|
|
||||||
* `floor value` – operacja podobna do matematycznej funkcji podłogi (zaokrąglenie liczby do pierwszej liczby całkowitej większej lub równej tej liczbie);
|
|
||||||
- `value` – musi być to zmienna o typie Number lub tablica takich zmiennych;
|
|
||||||
|
|
||||||
* `fold args` – używa elementów tablicy jako argumentów podanej funkcji:
|
|
||||||
- `args` – postaci `tablica funkcja` lub `tablica wartość_startowa funkcja`;
|
|
||||||
|
|
||||||
* `for vect` – iteruje po elementach wektora `vect`:
|
|
||||||
- `vect` - kontener wartości, musi posiadać typ Vector;
|
|
||||||
|
|
||||||
* `hash vect` – standardowa funkcja haszująca, zwraca jeden hash połączonych wartości z `vect`:
|
|
||||||
- `vect` - kontener wartości, mogą być dowolnego typu;
|
|
||||||
|
|
||||||
* `if cond [if_true] [if_false]` – wyrażenie warunkowe: jeżeli `cond` będzie prawdą, zostanie wykonany kod z `[if_true]`, w przeciwnym wypadku wykonany zostanie kod z `[if_false]` – fragment `[if_false]` jest opcjonalny;
|
|
||||||
|
|
||||||
* `incoming args` – pozwala na rozpatrzenie przychodzących komunikatów MIDI (`note_on` i `note_off`), odpowiednio;
|
|
||||||
- `args` – konstrukcja `komunikat, nuta`;
|
|
||||||
|
|
||||||
* `instrument args` – pozwala na zmianę instrumentu:
|
|
||||||
- `args` – może przyjmować sam numer programu, lub parę `numer_programu, kanał`;
|
|
||||||
|
|
||||||
* `len args` – zwraca długość kontenera `args`, a jeżeli `args` nie jest wektorem ustawia domyślną długość trwania dźwięku, domyślnie ćwierćnuta;
|
|
||||||
|
|
||||||
* `max args` – zwraca maksimum z `args`;
|
|
||||||
|
|
||||||
* `min args` – zwraca minimum z `args`;
|
|
||||||
|
|
||||||
* `mix args` – algorytmicznie miesza wszystkie elementy z `args`:
|
|
||||||
- `args` – tablica elementów, może być tablicą z zagnieżdżeniami;
|
|
||||||
|
|
||||||
* `note_off args` – w zależności od kształtu `args`:
|
|
||||||
- jeżeli `args` są w postaci `(kanał, nuta)` – wyłącza nutę na danym kanale:
|
|
||||||
- `kanał` – liczba całkowita;
|
|
||||||
- `nuta` – postać podobna do `notes` z `chord notes`;
|
|
||||||
- jeżeli `args` są w postaci `(kanał, akord)` – wyłącza wszystkie nuty z danego akordu na danym kanale;
|
|
||||||
|
|
||||||
* `note_on args` – analogicznie do `note_off args`;
|
|
||||||
|
|
||||||
* `nprimes value` – generuje `value` kolejnych liczb pierwszych:
|
|
||||||
- `value` – musi być typu Number;
|
|
||||||
|
|
||||||
* `oct value` – analogicznie do `bpm value`, wartość domyślna to 4;
|
|
||||||
|
|
||||||
* `par args` – gra współbieżnie pierwszy dźwięk z `args` z pozostałymi dźwiękami z `args`:
|
|
||||||
- `args` – postać `(note, ...)`, powinien być rozmiaru co najmniej 2:
|
|
||||||
- `note` – postać podobna do `notes` z `chords notes`;
|
|
||||||
|
|
||||||
* `partition args` – dzieli `args` na dwie grupy wedle danej funkcji:
|
|
||||||
- `args` – powinno przyjąć formę `(funkcja, tablica())`
|
|
||||||
|
|
||||||
* `permute args` – permutuje `args`:
|
|
||||||
- `args` – tablica obiektów;
|
|
||||||
|
|
||||||
* `pgmchange args` – analogicznie do `instrument args`;
|
|
||||||
|
|
||||||
* `play args` – gra `args`:
|
|
||||||
- `args` – mogą być to pojedyncze nuty, tablica nut oraz bloki kodu (nuty analogicznie jak w `chord notes`);
|
|
||||||
|
|
||||||
* `program_change args` – analogicznie do `instrument args`;
|
|
||||||
|
|
||||||
* `range args` – zwraca tablicę wartości liczbowych w podanych w `args` zakresie:
|
|
||||||
- `args` – postać `stop`, `start stop` lub `start stop step`;
|
|
||||||
|
|
||||||
* `reverse args` – odwraca kolejność elementów `args`;
|
|
||||||
- `args` – powinna być to tablica;
|
|
||||||
|
|
||||||
* `rotate args` – przenosi na koniec tablicy wskazaną ilość elementów:
|
|
||||||
- `args` – musi być postaci `liczba tablica`;
|
|
||||||
|
|
||||||
* `round value` – zaokrągla wartość zgodnie z reguałmi matematyki:
|
|
||||||
- `value` – musi być to wartość liczbowa;
|
|
||||||
|
|
||||||
* `shuffle args` – tasuje elementy `args`:
|
|
||||||
- `args` – powinna być to tablica;
|
|
||||||
|
|
||||||
* `sim` – #TODO
|
|
||||||
|
|
||||||
* `sort args` – sortuje elementy `args`:
|
|
||||||
- `args` – powinna być to tablica;
|
|
||||||
|
|
||||||
* `try args` – próbuje wykonać wszystkie bloki kodu poza ostatnim, a jeżeli w trakcie tej próby natrafi na błąd, wykonuje ostatni blok:
|
|
||||||
- `args` – musi być to co najmniej jeden blok kodu Musique;
|
|
||||||
|
|
||||||
* `typeof variable` – zwraca typ wskazanej `variable`;
|
|
||||||
|
|
||||||
* `uniq args` – zwraca tablicę elementów, z której usunięto następujące po sobie powtórzenia:
|
|
||||||
- `args` – powinna być to tablica;
|
|
||||||
|
|
||||||
* `unique args` – zwraca tablicę elementów, z której usunięto powtórzenia:
|
|
||||||
- `args` – powinna być to tablica;
|
|
||||||
|
|
||||||
* `up value` – analogicznie do `down value`;
|
|
||||||
|
|
||||||
* `update args` – aktualizuje element tablicy do nowej wartości:
|
|
||||||
- `args` – postaci `tablica indeks wartość`;
|
|
||||||
|
|
@ -11,6 +11,14 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
/// This macro implements functions that are only implemented as forwarding
|
||||||
|
/// all arguments to another function
|
||||||
|
#define Forward_Implementation(New_Function_Name, Implementation) \
|
||||||
|
static inline Result<Value> New_Function_Name(Interpreter &interpreter, std::vector<Value> args) \
|
||||||
|
{ \
|
||||||
|
return Implementation(interpreter, std::move(args)); \
|
||||||
|
}
|
||||||
|
|
||||||
/// 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) {
|
||||||
@ -103,6 +111,39 @@ static Result<Value> ctx_read_write_property(Interpreter &interpreter, std::vect
|
|||||||
return Value{};
|
return Value{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//: Funkcja `bpm` pozwala na zapisywanie i odczytywanie wartości BPM z aktualnego kontekstu.
|
||||||
|
//:
|
||||||
|
//: Domyślną wartością jest 120.
|
||||||
|
//: # Odczytywanie wartości z kontekstu
|
||||||
|
//: ```
|
||||||
|
//: > call bpm
|
||||||
|
//: 120
|
||||||
|
//: ```
|
||||||
|
//: # Zapisywanie wartości BPM do aktualnego kontekstu
|
||||||
|
//: ```
|
||||||
|
//: > bpm 144
|
||||||
|
//: 144
|
||||||
|
//: ```
|
||||||
|
Forward_Implementation(builtin_bpm, ctx_read_write_property<&Context::bpm>)
|
||||||
|
|
||||||
|
//: Funkcja `oct` pozwala na zapisywanie i odczytywanie wartości oktawy z aktualnego kontekstu.
|
||||||
|
//:
|
||||||
|
//: Wartość ta jest używana w momencie odtwarzania dźwięków nie posiadających ustalonego numeru oktawy:
|
||||||
|
//: `c` zostanie uzupełnione oktawą domyślną z kontekstu, `c5` zachowa swój nr oktawy.
|
||||||
|
//:
|
||||||
|
//: Domyślną wartością jest 120.
|
||||||
|
//: # Odczytywanie wartości z kontekstu
|
||||||
|
//: ```
|
||||||
|
//: > call bpm
|
||||||
|
//: 120
|
||||||
|
//: ```
|
||||||
|
//: # Zapisywanie wartości BPM do aktualnego kontekstu
|
||||||
|
//: ```
|
||||||
|
//: > bpm 144
|
||||||
|
//: 144
|
||||||
|
//: ```
|
||||||
|
Forward_Implementation(builtin_oct, ctx_read_write_property<&Context::octave>)
|
||||||
|
|
||||||
/// Iterate over array and it's subarrays to create one flat array
|
/// Iterate over array and it's subarrays to create one flat array
|
||||||
static Result<Array> into_flat_array(Interpreter &interpreter, std::span<Value> args)
|
static Result<Array> into_flat_array(Interpreter &interpreter, std::span<Value> args)
|
||||||
{
|
{
|
||||||
@ -172,12 +213,16 @@ invalid_argument_type:
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Forward_Implementation(builtin_ceil, apply_numeric_transform<&Number::ceil>)
|
||||||
|
Forward_Implementation(builtin_floor, apply_numeric_transform<&Number::floor>)
|
||||||
|
Forward_Implementation(builtin_round, apply_numeric_transform<&Number::round>)
|
||||||
|
|
||||||
/// Direction used in range definition (up -> 1, 2, 3; down -> 3, 2, 1)
|
/// Direction used in range definition (up -> 1, 2, 3; down -> 3, 2, 1)
|
||||||
enum class Range_Direction { Up, Down };
|
enum class Range_Direction { Up, Down };
|
||||||
|
|
||||||
/// Create range according to direction and specification, similar to python
|
/// Create range according to direction and specification, similar to python
|
||||||
template<Range_Direction dir>
|
template<Range_Direction dir>
|
||||||
static Result<Value> builtin_range(Interpreter&, std::vector<Value> args)
|
static Result<Value> range(Interpreter&, std::vector<Value> args)
|
||||||
{
|
{
|
||||||
auto start = Number(0), stop = Number(0), step = Number(1);
|
auto start = Number(0), stop = Number(0), step = Number(1);
|
||||||
|
|
||||||
@ -210,7 +255,20 @@ static Result<Value> builtin_range(Interpreter&, std::vector<Value> args)
|
|||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send MIDI Program Change message
|
Forward_Implementation(builtin_range, range<Range_Direction::Up>)
|
||||||
|
Forward_Implementation(builtin_up, range<Range_Direction::Up>)
|
||||||
|
Forward_Implementation(builtin_down, range<Range_Direction::Down>)
|
||||||
|
|
||||||
|
//: Funkcja `instrument` pozwala na wybór instrumentu na danym kanale MIDI.
|
||||||
|
//: # Ustawienie instrumentu 4
|
||||||
|
//: ```
|
||||||
|
//: instrument 4
|
||||||
|
//: ```
|
||||||
|
//: # Ustawienie instrumentu 4 na kanale 6
|
||||||
|
//: ```
|
||||||
|
//: instrument 6 4
|
||||||
|
//: ```
|
||||||
|
//: Przyporządkowanie numerów instrumentów do standardowych nazw znajdziesz [tutaj](http://midi.teragonaudio.com/tutr/gm.htm#Patch)
|
||||||
static auto builtin_program_change(Interpreter &i, std::vector<Value> args) -> Result<Value> {
|
static auto builtin_program_change(Interpreter &i, std::vector<Value> args) -> Result<Value> {
|
||||||
if (auto a = match<Number>(args)) {
|
if (auto a = match<Number>(args)) {
|
||||||
auto [program] = *a;
|
auto [program] = *a;
|
||||||
@ -1113,14 +1171,14 @@ void Interpreter::register_builtin_functions()
|
|||||||
{
|
{
|
||||||
auto &global = *Env::global;
|
auto &global = *Env::global;
|
||||||
|
|
||||||
global.force_define("bpm", ctx_read_write_property<&Context::bpm>);
|
global.force_define("bpm", builtin_bpm);
|
||||||
global.force_define("call", builtin_call);
|
global.force_define("call", builtin_call);
|
||||||
global.force_define("ceil", apply_numeric_transform<&Number::ceil>);
|
global.force_define("ceil", builtin_ceil);
|
||||||
global.force_define("chord", builtin_chord);
|
global.force_define("chord", builtin_chord);
|
||||||
global.force_define("down", builtin_range<Range_Direction::Down>);
|
global.force_define("down", builtin_down);
|
||||||
global.force_define("duration", builtin_duration);
|
global.force_define("duration", builtin_duration);
|
||||||
global.force_define("flat", builtin_flat);
|
global.force_define("flat", builtin_flat);
|
||||||
global.force_define("floor", apply_numeric_transform<&Number::floor>);
|
global.force_define("floor", builtin_floor);
|
||||||
global.force_define("fold", builtin_fold);
|
global.force_define("fold", builtin_fold);
|
||||||
global.force_define("for", builtin_for);
|
global.force_define("for", builtin_for);
|
||||||
global.force_define("hash", builtin_hash);
|
global.force_define("hash", builtin_hash);
|
||||||
@ -1134,7 +1192,7 @@ void Interpreter::register_builtin_functions()
|
|||||||
global.force_define("note_off", builtin_note_off);
|
global.force_define("note_off", builtin_note_off);
|
||||||
global.force_define("note_on", builtin_note_on);
|
global.force_define("note_on", builtin_note_on);
|
||||||
global.force_define("nprimes", builtin_primes);
|
global.force_define("nprimes", builtin_primes);
|
||||||
global.force_define("oct", ctx_read_write_property<&Context::octave>);
|
global.force_define("oct", builtin_oct);
|
||||||
global.force_define("par", builtin_par);
|
global.force_define("par", builtin_par);
|
||||||
global.force_define("partition", builtin_partition);
|
global.force_define("partition", builtin_partition);
|
||||||
global.force_define("permute", builtin_permute);
|
global.force_define("permute", builtin_permute);
|
||||||
@ -1142,10 +1200,10 @@ void Interpreter::register_builtin_functions()
|
|||||||
global.force_define("pick", builtin_pick);
|
global.force_define("pick", builtin_pick);
|
||||||
global.force_define("play", builtin_play);
|
global.force_define("play", builtin_play);
|
||||||
global.force_define("program_change", builtin_program_change);
|
global.force_define("program_change", builtin_program_change);
|
||||||
global.force_define("range", builtin_range<Range_Direction::Up>);
|
global.force_define("range", builtin_range);
|
||||||
global.force_define("reverse", builtin_reverse);
|
global.force_define("reverse", builtin_reverse);
|
||||||
global.force_define("rotate", builtin_rotate);
|
global.force_define("rotate", builtin_rotate);
|
||||||
global.force_define("round", apply_numeric_transform<&Number::round>);
|
global.force_define("round", builtin_round);
|
||||||
global.force_define("scan", builtin_scan);
|
global.force_define("scan", builtin_scan);
|
||||||
global.force_define("set_len", builtin_set_len);
|
global.force_define("set_len", builtin_set_len);
|
||||||
global.force_define("set_oct", builtin_set_oct);
|
global.force_define("set_oct", builtin_set_oct);
|
||||||
@ -1156,7 +1214,7 @@ void Interpreter::register_builtin_functions()
|
|||||||
global.force_define("typeof", builtin_typeof);
|
global.force_define("typeof", builtin_typeof);
|
||||||
global.force_define("uniq", builtin_uniq);
|
global.force_define("uniq", builtin_uniq);
|
||||||
global.force_define("unique", builtin_unique);
|
global.force_define("unique", builtin_unique);
|
||||||
global.force_define("up", builtin_range<Range_Direction::Up>);
|
global.force_define("up", builtin_up);
|
||||||
global.force_define("update", builtin_update);
|
global.force_define("update", builtin_update);
|
||||||
global.force_define("while", builtin_while);
|
global.force_define("while", builtin_while);
|
||||||
}
|
}
|
||||||
|
256
scripts/document-builtin.py
Executable file
256
scripts/document-builtin.py
Executable file
@ -0,0 +1,256 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import dataclasses
|
||||||
|
import string
|
||||||
|
import re
|
||||||
|
import itertools
|
||||||
|
import typing
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
MARKDOWN_CONVERTER = "lowdown -m 'shiftheadinglevelby=3'"
|
||||||
|
CPP_FUNC_IDENT_ALLOWLIST = string.ascii_letters + string.digits + "_"
|
||||||
|
PROGRAM_NAME: str = ""
|
||||||
|
|
||||||
|
HTML_PREFIX = """<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Dokumentacja funkcji języka Musique</title>
|
||||||
|
<style>
|
||||||
|
html {
|
||||||
|
color: #1a1a1a;
|
||||||
|
background-color: #fdfdfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
margin: 10px auto;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
font-kerning: normal;
|
||||||
|
max-width: 50em;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a, nav a:link, nav a:visited {
|
||||||
|
color: #07a;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 > a, h2>a:visited, h2>a:link {
|
||||||
|
color: gray;
|
||||||
|
text-decoration: none;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
h2 > a:hover { color: lightgrey; }
|
||||||
|
|
||||||
|
footer {
|
||||||
|
width: 100%;
|
||||||
|
border-top: 1px solid black;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Dokumentacja funkcji języka Musique</h1>
|
||||||
|
"""
|
||||||
|
|
||||||
|
HTML_SUFFIX = """
|
||||||
|
<footer>
|
||||||
|
Wszystkie treści podlegają licencji <a href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0</a>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def warning(*args, prefix: str | None = None):
|
||||||
|
if prefix is None:
|
||||||
|
prefix = PROGRAM_NAME
|
||||||
|
message = ": ".join(itertools.chain([prefix, "warning"], args))
|
||||||
|
print(message)
|
||||||
|
|
||||||
|
|
||||||
|
def error(*args, prefix=None):
|
||||||
|
if prefix is None:
|
||||||
|
prefix = PROGRAM_NAME
|
||||||
|
message = ": ".join(itertools.chain([prefix, "error"], args))
|
||||||
|
print(message)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Builtin:
|
||||||
|
implementation: str
|
||||||
|
definition_location: tuple[str, int] # Filename and line number
|
||||||
|
names: list[str]
|
||||||
|
documentation: str
|
||||||
|
|
||||||
|
|
||||||
|
def builtins_from_file(source_path: str) -> typing.Generator[Builtin, None, None]:
|
||||||
|
with open(source_path) as f:
|
||||||
|
source = f.readlines()
|
||||||
|
|
||||||
|
builtins: dict[str, Builtin] = {}
|
||||||
|
definition = re.compile(
|
||||||
|
r"""force_define.*\("([^"]+)"\s*,\s*(builtin_[a-zA-Z0-9_]+)\)"""
|
||||||
|
)
|
||||||
|
|
||||||
|
current_documentation = []
|
||||||
|
|
||||||
|
for lineno, line in enumerate(source):
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
# Check if line contains force_define with static string and builtin_*
|
||||||
|
# thats beeing defined. It's a one of many names that given builtin
|
||||||
|
# has in Musique
|
||||||
|
if result := definition.search(line):
|
||||||
|
musique_name = result.group(1)
|
||||||
|
builtin_name = result.group(2)
|
||||||
|
|
||||||
|
if builtin_name in builtins:
|
||||||
|
builtins[builtin_name].names.append(musique_name)
|
||||||
|
else:
|
||||||
|
error(
|
||||||
|
f"tried adding Musique name '{musique_name}' to builtin '{builtin_name}' that has not been defined yet",
|
||||||
|
prefix=f"{source_path}:{lineno}",
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check if line contains special documentation comment.
|
||||||
|
# We assume that only documentation comments are in given line (modulo whitespace)
|
||||||
|
if line.startswith("//:"):
|
||||||
|
line = line.removeprefix("//:").strip()
|
||||||
|
current_documentation.append(line)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check if line contains builtin_* identifier.
|
||||||
|
# If contains then this must be first definition of this function
|
||||||
|
# and therefore all documentation comments before it describe it
|
||||||
|
if (index := line.find("builtin_")) >= 0 and line[
|
||||||
|
index - 1
|
||||||
|
] not in CPP_FUNC_IDENT_ALLOWLIST:
|
||||||
|
identifier = line[index:]
|
||||||
|
for i, char in enumerate(identifier):
|
||||||
|
if char not in CPP_FUNC_IDENT_ALLOWLIST:
|
||||||
|
identifier = identifier[:i]
|
||||||
|
break
|
||||||
|
|
||||||
|
if identifier not in builtins:
|
||||||
|
builtin = Builtin(
|
||||||
|
implementation=identifier,
|
||||||
|
# TODO Allow redefinition of source path with some prefix
|
||||||
|
# to allow website links
|
||||||
|
definition_location=(source_path, lineno),
|
||||||
|
names=[],
|
||||||
|
documentation="\n".join(current_documentation),
|
||||||
|
)
|
||||||
|
builtins[identifier] = builtin
|
||||||
|
current_documentation = []
|
||||||
|
continue
|
||||||
|
|
||||||
|
for builtin in builtins.values():
|
||||||
|
builtin.names.sort()
|
||||||
|
yield builtin
|
||||||
|
|
||||||
|
|
||||||
|
def filter_builtins(builtins: list[Builtin]) -> typing.Generator[Builtin, None, None]:
|
||||||
|
for builtin in builtins:
|
||||||
|
if not builtin.documentation:
|
||||||
|
warning(f"builtin '{builtin.implementation}' doesn't have documentation")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Testt if builtin is unused
|
||||||
|
if not builtin.names:
|
||||||
|
continue
|
||||||
|
|
||||||
|
yield builtin
|
||||||
|
|
||||||
|
|
||||||
|
def each_musique_name_occurs_once(
|
||||||
|
builtins: typing.Iterable[Builtin],
|
||||||
|
) -> typing.Generator[Builtin, None, None]:
|
||||||
|
names = {}
|
||||||
|
for builtin in builtins:
|
||||||
|
for name in builtin.names:
|
||||||
|
if name in names:
|
||||||
|
error(
|
||||||
|
f"'{name}' has been registered as both '{builtin.implementation}' and '{names[name]}'"
|
||||||
|
)
|
||||||
|
names[name] = builtin.implementation
|
||||||
|
yield builtin
|
||||||
|
|
||||||
|
|
||||||
|
def generate_html_document(builtins: list[Builtin], output_path: str):
|
||||||
|
with open(output_path, "w") as out:
|
||||||
|
out.write(HTML_PREFIX)
|
||||||
|
|
||||||
|
out.write("<nav>")
|
||||||
|
|
||||||
|
names = sorted(
|
||||||
|
[
|
||||||
|
(name, builtin.implementation)
|
||||||
|
for builtin in builtins
|
||||||
|
for name in builtin.names
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
out.write(", ".join(f"""<a href="#{id}">{name}</a>""" for name, id in names))
|
||||||
|
|
||||||
|
out.write("</nav>")
|
||||||
|
|
||||||
|
out.write("<main>")
|
||||||
|
|
||||||
|
for builtin in builtins:
|
||||||
|
out.write("<p>")
|
||||||
|
out.write(
|
||||||
|
f"""<h2 id="{builtin.implementation}"><a href="#{builtin.implementation}">§</a>{', '.join(builtin.names)}</h2>"""
|
||||||
|
)
|
||||||
|
out.write(
|
||||||
|
subprocess.check_output(
|
||||||
|
MARKDOWN_CONVERTER,
|
||||||
|
input=builtin.documentation,
|
||||||
|
encoding="utf-8",
|
||||||
|
shell=True,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
out.write("</p>")
|
||||||
|
|
||||||
|
out.write("</main>")
|
||||||
|
|
||||||
|
out.write(HTML_SUFFIX)
|
||||||
|
|
||||||
|
|
||||||
|
def main(source_path: str, output_path: str):
|
||||||
|
"Generates documentaiton from file source_path and saves in output_path"
|
||||||
|
|
||||||
|
builtins = builtins_from_file(source_path)
|
||||||
|
builtins = filter_builtins(builtins)
|
||||||
|
builtins = each_musique_name_occurs_once(builtins)
|
||||||
|
builtins = sorted(list(builtins), key=lambda builtin: builtin.names[0])
|
||||||
|
|
||||||
|
generate_html_document(builtins, output_path)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Builtin functions documentation generator from C++ Musique implementation"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"source",
|
||||||
|
type=str,
|
||||||
|
nargs=1,
|
||||||
|
help="C++ source file from which documentation will be generated",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-o",
|
||||||
|
"--output",
|
||||||
|
type=str,
|
||||||
|
nargs=1,
|
||||||
|
required=True,
|
||||||
|
help="path for standalone HTML file containing generated documentation",
|
||||||
|
)
|
||||||
|
|
||||||
|
PROGRAM_NAME = parser.prog
|
||||||
|
args = parser.parse_args()
|
||||||
|
assert len(args.source) == 1
|
||||||
|
assert len(args.output) == 1
|
||||||
|
|
||||||
|
main(source_path=args.source[0], output_path=args.output[0])
|
@ -1,45 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# This script creates a release of Musique programming language
|
|
||||||
# Release is defined as a zip archive containing source code,
|
|
||||||
# build binaries for supported platforms and build documentation
|
|
||||||
|
|
||||||
set -e -o pipefail
|
|
||||||
|
|
||||||
Suffix="$(date +"%Y-%m-%d")"
|
|
||||||
Target="release_$Suffix"
|
|
||||||
Image="musique-builder"
|
|
||||||
|
|
||||||
if [ -d "$Target" ]; then
|
|
||||||
rm -rf "$Target"
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p "$Target"
|
|
||||||
|
|
||||||
if [[ "$(docker images -q "$Image")" == "" ]]; then
|
|
||||||
docker build -t "$Image" .
|
|
||||||
fi
|
|
||||||
sudo rm -rf bin/
|
|
||||||
docker run -it --rm -v "$(pwd):/musique" -w /musique "$Image" make os=linux CC=gcc-11 CXX=g++-11 >/dev/null
|
|
||||||
|
|
||||||
cp bin/musique "$Target"/musique-x86_64-linux
|
|
||||||
|
|
||||||
sudo rm -rf bin/
|
|
||||||
make os=windows >/dev/null
|
|
||||||
cp bin/musique.exe "$Target"/musique-x86_64-windows.exe
|
|
||||||
|
|
||||||
cp LICENSE "$Target"/LICENSE
|
|
||||||
cp CHANGELOG.md "$Target/CHANGELOG.md"
|
|
||||||
cp -r examples "$Target"/examples
|
|
||||||
sed "s/bin\/musique/musique/" scripts/install > "$Target"/install.sh
|
|
||||||
chmod 0755 "$Target"/install.sh
|
|
||||||
|
|
||||||
lowdown -s doc/functions.md -m "title:Lista funkcji języka Musique" -o "$Target"/functions.html
|
|
||||||
python scripts/language-cmp-cheatsheet.py doc/musique-vs-languages-cheatsheet.template
|
|
||||||
mv doc/musique-vs-languages-cheatsheet.html "${Target}/musique-vs-others-cheatsheet.html"
|
|
||||||
make doc/wprowadzenie.html && mv doc/wprowadzenie.html "${Target}/wprowadzenie.html"
|
|
||||||
|
|
||||||
git clone --recursive --quiet --depth=1 "$(git remote -v | awk '{ print $2 }' | head -n1)" "$Target"/source_code
|
|
||||||
rm -rf "$Target"/source_code/.git
|
|
||||||
|
|
||||||
zip -q -r "musique_$Suffix.zip" "$Target"/*
|
|
Loading…
Reference in New Issue
Block a user