diff --git a/.gitignore b/.gitignore index 9455005..759bc5e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ drafts.cc .cache release_* *.zip +*.html diff --git a/doc/musique-vs-languages-cheatsheet.template b/doc/musique-vs-languages-cheatsheet.template new file mode 100644 index 0000000..c829742 --- /dev/null +++ b/doc/musique-vs-languages-cheatsheet.template @@ -0,0 +1,121 @@ +TITLE Porównanie Musique z typowymi językami programowania + +BEGIN CSS +table, tr, td { + font-size: 12pt; + border: 1pt solid #DDDDDD; + border-collapse: collapse; + vertical-align: top; +} + +td, th { + padding: 2px; +} + +END CSS + +BEGIN INTRO +

Ściągawka Musique, a Python i Ruby

+Ponieważ Musique jest kierowany do osób posiadających doświadczenie z muzyką algorytmiczną, szybkim sposobem na podstawowe poznanie języka jest porównanie z innymi technologiami w tej dziedzinie. Ten dokument służy bardziej ukazaniu różnic niż omówieniu samego języka. +END INTRO + +BEGIN TABLE + +n Kategoria +m Musique +p Python +r Ruby (SonicPi) +c Komentarz + +n Deklaracja +m x := 0 +p x = 0 +r x = 0 +c Zmienne należy zadeklarować by móc z nich korzystać + +n Aktualizacja +m x = 1 +r x = 1 +p x = 1 + +n Operacje matematyczne +m x = 10 * 30 - 40 ** 2 +p x = 10 * 30 - 40 ** 2 +r x = 10 * 30 - 40 ** 2 +c Zarówno Python, Ruby jak i Musique posiadają operator potęgi ** + +n Funkcja anonimowa +m add := [x y | x + y] +p add = lambda x, y: x + y +r add = ->(x, y) { x + y } + +n Deklaracja funkcji +m add := [x y | +m say x y; +m x + y +m ] +p def add(x, y): +p print(x, y) +p return x + y +r def add(x, y) +r puts x, y +r return x + y +r end +c Musique nie rozróżnia funkcji anonimowych i zadeklarowanych + +n Tablice +m x = [1; 2; 3; 4] +p x = [1, 2, 3, 4] +r x = [1, 2, 3, 4] + +n Nty element tablicy +m x.n +p x[n] +r x[n] + +n Aktualizacja ntego elementu tablicy +m x = update x n 10 +p x[n] = 10 +r x[n] = 10 + +n Tablica od 0 do 9 włącznie +m x = up 10 +p x = range(10) +r x = [*0..9] + +n Tablica od 1 do 10 włącznie +m x = 1 + up 10 +m lub x = range 1 11 +p x = range(1, 11) +r x = [*1..10] + +n Iloczyn elementów tablicy +m fold '* (1 + up 5) +p functools.reduce( +p operator.mul, range(1, 6), 1) +r [*1..5].inject(:*) +c Musique pozwala zmienić dowolny operator w funkcję +c poprzez zapis 'operator jak '* w przykładzie + +n Instrukcja warunkowa +m if (n == 42) +m [ say 1 ] +m [ say 2 ] +p if n == 42: +p print(1) +p else: +p print(2) +r if n == 42 +r puts 1 +r else +r puts 2 +r end +c Musique nie posiada instrukcji warunkowej, a funkcję if, +c która przyjmuje wartość logiczną i dwa bloki (funkcje anonimowe) +c - jeden z nich zostanie wykonany jeśli warunek jest prawdziwy, +c a drugi jeśli jest fałszywy. +n Wyrażenie warunkowe +m x := if (n == 42) [1] [2] +p x = 1 if n == 42 else 2 +r x = n == 42 ? 1 : 2 +END TABLE diff --git a/scripts/language-cmp-cheatsheet.py b/scripts/language-cmp-cheatsheet.py new file mode 100644 index 0000000..d53ceec --- /dev/null +++ b/scripts/language-cmp-cheatsheet.py @@ -0,0 +1,140 @@ +import argparse +import os +import string +import collections + +Directive = collections.namedtuple("Directive", "line_number type content") + +HTML_TEMPLATE = string.Template(""" + + + + $title + + + +

$intro

+ $table
+ + +""") + +def parse_table(lines: list): + previus_column_type = None + rows, order = [{}], [] + current_row = rows[0] + + # Each nonblank matches this regular expression /(\S*)\s(.*)/ + # where first capture is type of column (essentialy column id) and + # second capture is given cell content. Where column type repeats not in row + # we have new row. + for line in lines: + if not line: + continue + + sep = line.find(' ') + column_type, cell_content = line[:sep].strip(), line[sep:] + + if column_type not in order: + order.append(column_type) + + if previus_column_type != column_type and column_type in current_row: + rows.append({}) + current_row = rows[-1] + + cell = current_row.get(column_type, []) + cell.append(cell_content) + current_row[column_type] = cell + previus_column_type = column_type + + # Eliminate common whitespace prefix in given column type (not all whitespace + # prefix since examples in Python may have significant whitespace) + for row in rows: + for cell in row.values(): + prefix_whitespace = min(len(s) - len(s.lstrip()) for s in cell) + for i, s in enumerate(cell): + cell[i] = s[prefix_whitespace:] + + return rows, order + +def compile_template(*, template_path: str, target_path: str): + # Read template file and separate it into lines + with open(template_path) as f: + template = [line.strip() for line in f.readlines()] + + directives = [] + for i, line in enumerate(template): + s = line.split() + if s and s[0] in ("BEGIN", "END", "TITLE"): + directives.append(Directive(i, s[0], line[len(s[0]):].strip())) + + title, css_source, table_source, intro_source = 4 * [None] + + for directive in directives: + if directive.type == "TITLE": + title = directive.content + continue + + if directive.type == "BEGIN": + start_line = directive.line_number + for end in directives: + if end.type == "END" and end.content == directive.content: + end_line = end.line_number + break + else: + assert False, "Begin without matching end" + + span = template[start_line+1:end_line] + + if directive.content == "CSS": + css_source = span + elif directive.content == "TABLE": + table_source = span + elif directive.content == "INTRO": + intro_source = span + + assert css_source is not None + assert table_source is not None + assert intro_source is not None + assert title is not None + + rows, columns_order = parse_table(lines=table_source) + + table = "" + + for i, row in enumerate(rows): + if i == 0: + line = ["" + ' '.join(row[k]) + "" for k in columns_order] + else: + line = [] + for column in columns_order: + val = row.get(column, []) + line.append('
' + '
'.join(val) + '
') + + table += "\n" + '\n'.join(line) + "\n\n" + + + final = HTML_TEMPLATE.substitute({ + "title": title, + "css": "\n".join(css_source), + "table": table, + "intro": "\n".join(intro_source) + }) + + with open(target_path, "w") as f: + f.write(final) + + +def main(): + parser = argparse.ArgumentParser(description="Build language comparison chart") + parser.add_argument(nargs='+', metavar="TEMPLATE", dest="templates", help="Template file that will be converted to HTML page") + + args = parser.parse_args() + + for template in args.templates: + dst, _ = os.path.splitext(template) + dst += ".html" + compile_template(template_path=template, target_path=dst) + +if __name__ == "__main__": + main()