Autor Thema: ly -> JSON ?  (Gelesen 3206 mal)

jps

  • Member
ly -> JSON ?
« am: Samstag, 5. März 2016, 20:34 »
Guten Abend!

Als 2. Scheme-Etüde (nicht besonders musikspezifisch) habe ich nun mal den Rahmen für ein Miniprogramm zusammengeschraubt, das Infos aus einer Lilypond-Datei (die im Normalfall natürlich aus den musikalischen Kontexten einzusammeln wären) in eine JSON-Datei schreiben könnte - zwecks leichteren Datenaustausches, zur Weiterverarbeitung in anderen Programmen (z.B. auch im Browser).

Auch, wenn es offenbar auf diese Weise funktioniert, bin ich für jegliche Hinweise, die vielleicht zu einem besseren und eleganteren Code führen, dankbar.

Insbesondere haben sich die folgenden Fragen ergeben:

1. Kann man den absoluten Pfad zur aktuellen Datei tatsächlich nur über den Zugriff auf die command line auslesen, oder geht das auch direkter?

2. Wie kann man in Scheme/Guile einen garantiert universell funktionierenden path separator ansprechen? – Aus der guile-Referenz bin ich da nicht ganz schlau geworden.

3. Gibt es evtl. schon so etwas à la „ly2json“? - Dann bräuchte man das Rad ja nicht neu zu erfinden.

\version "2.19.37"


% Zur Übung sollen hier die Elemente aus der folgenden Liste in eine JSON-Datei kopiert werden,
% die im gleichen Verzeichnis liegen soll wie die vorliegende Lilypond-Datei:


#(define elemente_als_liste '("Element 1"
                              "Element 2"
                              "Element 3"
                              "Element 4"
                              "Element 5"
                              "Element 6"
                              "Element 7"
                              "Element 8"))


% Die Funktion "command-line" gibt offenbar eine in Klammern eingefasste String-Liste zurück,
% deren letztes Element der absolute Dateipfad der vorliegenden Lilypond-Datei ist:


#(define (generiere_neuen_absoluten_pfad_zu_einer_js_datei_im_gleichen_verzeichnis)
  (let ((kommandozeile (object->string (command-line))))
    (string-append
      (substring kommandozeile
        (+ (string-rindex kommandozeile #\sp) 2)
        (+ (string-rindex kommandozeile #\/) 1))
        "Und hier ist das neue JSON-Objekt.js")))


#(define (erstelle_ein_json_objekt_und_schreibe_es_in_die_datei)
   
  (let ((elemente_als_string "")
        (ausgabe ""))
   
  ; In rekursiven Aufrufen entnimmt die folgende Funktion ein Element nach dem anderen von vorne aus der Liste
  ; und fügt die Elemente zu einem String zusammen (dazwischen jeweils: ", "):
   
  (define (verarzte_das_naechste_element_aus_der_liste aktuelle_liste)
    (cond ((null? aktuelle_liste) aktuelle_liste)
      (else
        (set! elemente_als_string
          (string-append elemente_als_string (car aktuelle_liste) "\", \""))
        (verarzte_das_naechste_element_aus_der_liste (cdr aktuelle_liste)))))
 
  ; Um daraus ein rechtschaffenes JSON-Objekt zu machen, sind noch ein paar umschließende Elemente erforderlich,
  ; wobei zuvor am Ende drei überflüssige Zeichen (, ") zu löschen sind:
 
  (define (stoepsele_das_json_objekt_zusammen) 
    (set! ausgabe 
      (string-append
        "{\n  \"Inhalt der Lilypond-Liste\":  [\""
        (substring elemente_als_string  0 (- (string-length  elemente_als_string) 3))
        "]\n}")))
 
  ; Der Parameter "w" beim Öffnen des Ausgabeports sorgt dafür, dass bereits vorhandene Dateiinhalte überschrieben werden
  ; (der Parameter "a" würde den neuen String an den vorhandenen Inhalt anhängen):
 
  (define (übertrage_das_neugeschaffene_kunstwerk_in_die_datei)
    (let (
      (ausgabeport
        (open-file
          (generiere_neuen_absoluten_pfad_zu_einer_js_datei_im_gleichen_verzeichnis) "w")))
      (format ausgabeport ausgabe)
      (close ausgabeport)
      (display "\nDie JSON-Datei wurde im gleichen Pfad wie die Lilypond-Datei erstellt (bzw. dort ueberschrieben).")))
 
  (verarzte_das_naechste_element_aus_der_liste elemente_als_liste)
  (stoepsele_das_json_objekt_zusammen)
  (übertrage_das_neugeschaffene_kunstwerk_in_die_datei)))
   

#(erstelle_ein_json_objekt_und_schreibe_es_in_die_datei)


Danke und herzliche Grüße
Jost

harm6

  • Member
Re: ly -> JSON ?
« Antwort #1 am: Samstag, 5. März 2016, 21:18 »
Hallo Jost,

Dein Code compiliert bei mir nicht:

Zitat
error: GUILE signaled an error for the expression beginning here
#
 (erstelle_ein_json_objekt_und_schreibe_es_in_die_datei)
Value out of range 54 to 67: 43

Ganz generell: es ist in guile üblich "-" in Namen zu verwenden und nicht "_". Das wäre eher was für C++

Bei
(verarzte_das_naechste_element_aus_der_liste aktuelle_liste)
kam mir string-join als wahrscheinlich simplere Vorgehensweise in den Sinn.

„ly2json“ gibt es meines Wissens nach nicht.

Mehr weiß ich im Moment nicht, ohne das file zum kompilieren zu bringen.
Aber so wie ich mich kenne würde ich es dann wohl komplett neu schreiben.
Wie David Kastrup mal sagte: "Dann weiß ich wenigstens wo die bugs sind" :D

Gruß,
  Harm

harm6

  • Member
Re: ly -> JSON ?
« Antwort #2 am: Samstag, 5. März 2016, 22:33 »
Folgendes scheint zu klappen:

\version "2.19.36"

#(define elemente-als-liste '("Element 1"
                              "Element 2"
                              "Element 3"
                              "Element 4"
                              "Element 5"
                              "Element 6"
                              "Element 7"
                              "Element 8"))
                             
#(define
  (neuer-absoluter-pfad-zu-einer-js-datei-im-gleichen-verzeichnis)
  (string-join (drop-right (string-split (last (command-line)) #\/) 1) "/"))
       
#(define my-name (ly:parser-output-name)) %% oder ein anderer deiner Wahl

#(let ((port
         (open-file
           (format #f
             "~a/~a.json"
             (neuer-absoluter-pfad-zu-einer-js-datei-im-gleichen-verzeichnis)
             my-name)
             "w")))
   (format port
     "{\n  \"Inhalt der Lilypond-Liste\":  [\"~a\"]\n}"
     (string-join elemente-als-liste "\", \""))
   (close port))

Aber zu Deinen Fragen weiß ich sonst nichts beizutragen. :(


Gruß,
  Harm

jps

  • Member
Re: ly -> JSON ?
« Antwort #3 am: Sonntag, 6. März 2016, 12:46 »
Das mir der fehlschlagenden Kompilierung ist eigenartig. Bei mir läuft es auf mehreren PCs auf Anhieb problemlos (wenn die ly-Datei in einem Verzeichnis liegt, auf das Zugriffsrechte bestehen, und wenn nicht auf ein temporäres Verzeichnis verwiesen wird). Deshalb kann ich auch zunächst mal nicht testen, woran der Fehler liegen könnte. Hab allerdings Lilypond bisher nur unter Windows, noch nicht unter Linux installiert. Ich bleib da mal dran.

Danke für die Alternativcodes an dieser und anderer Stelle. Die werde ich mir der Reihe nach mal in Ruhe zu Gemüte führen.

Herzliche Grüße
Jost

harm6

  • Member
Re: ly -> JSON ?
« Antwort #4 am: Sonntag, 6. März 2016, 13:22 »
Ich weiß, daß die Angabe von Dateipfaden auf verschiedenen OS durchaus sehr problematisch sein kann.
Ich bin auf Linux, insoweit würde es mich sehr interessieren, ob mein code auf Deinem windows-system funktioniert.
Oder ob wir uns gegenseitig code präsentieren, der für den jeweils anderen unbrauchbar ist...

Gruß,
  Harm

jps

  • Member
Re: ly -> JSON ?
« Antwort #5 am: Sonntag, 6. März 2016, 14:26 »
Sorry, das hatte ich vergessen zu erwähnen: Dein Code läuft auch unter Windows. Das hatte ich sofort probiert. Hab jetzt nur erst mal keine Zeit mehr, die Sachen inhaltlich durchzuarbeiten (was ich aber definitiv machen werde).

harm6

  • Member
Re: ly -> JSON ?
« Antwort #6 am: Sonntag, 6. März 2016, 15:38 »
Bei
#(write-me  "\n(command-line): \n"(command-line))
erhalte ich:
("/home/harm/lilypond-git/build/out/bin/lilypond" "atest-31.ly")
EDIT: zur Zeit des Aufrufs von (command-line) bin ich im selben Ordner wie atest-31.ly

Letztendlich versuchst Du dann daraus einen substring auszulesen von 51 bis 40.
Kann natürlich nicht klappen.

Ich schrieb ja schon über die Verwundbarkeit von substring. Aber warum überhaupt (zumindest so früh) einen string erzeugen und manipulieren.
Tatsächlich mußte ich 'object->string' nachschlagen, hatte es noch nie vorher verwendet.

(command-line) liefert ja eine Liste (von strings), diese Liste kann man ja erst mal bearbeiten.
Wenn man dann den string isoliert hat auf den es ankommt, dann kann man immer noch mit der string-manipulation anfangen.

Gruß,
  Harm
« Letzte Änderung: Sonntag, 6. März 2016, 15:54 von harm6 »

jps

  • Member
Re: ly -> JSON ?
« Antwort #7 am: Sonntag, 6. März 2016, 16:16 »
Dann ist ja klar, woran es liegt. Kann die Sache aktuell wie gesagt nicht wirklich aktiv weiterverfolgen.

In Eile nur:   #(write-me  "\n(command-line): \n"(command-line))
spuckt bei mir den absoluten Pfad komplett als letzten String aus:
"C:\\Program Files (x86)\\LilyPond\\usr\\bin\\lilypond-windows.exe" "-ddelete-intermediate-files" "-dpoint-and-click" "--pdf" "C:/Users/Jost_2/Desktop/ly2json_test.ly"

Daher kam mein Ansatz.

Übrigens habe ich jetzt gesehen, dass event-listener.ly (mit Lilypond mitgeliefert) es im Prinzip genauso macht wie ich (wenn ich das auf die Schnelle richtig sehe).
Das dürfte dann doch wahrscheinlich unter Linux auch nicht laufen?

Herzliche Grüße
Jost

harm6

  • Member
Re: ly -> JSON ?
« Antwort #8 am: Sonntag, 6. März 2016, 17:34 »
Zitat
Übrigens habe ich jetzt gesehen, dass event-listener.ly (mit Lilypond mitgeliefert) es im Prinzip genauso macht wie ich (wenn ich das auf die Schnelle richtig sehe).
Das dürfte dann doch wahrscheinlich unter Linux auch nicht laufen?

Auszug aus event-listener.ly:
Zitat
#(define (filename-from-staffname context)
   "Constructs a filename in the form
@file{@var{original_filename}-@var{staff_instrument_name}.notes} if the
staff has an instrument name.  If the staff has no instrument
name, it uses "unnamed-staff" for that part of the filename."
   (let* ((inst-name (ly:context-property context 'instrumentName)))
     (string-concatenate (list
                          (substring (object->string (command-line))
                           ;; filename without .ly part
                           (+ (string-rindex (object->string (command-line)) #\sp) 2)
                           (- (string-length (object->string (command-line))) 5))
                          "-"
                          (if (string? inst-name)
                              inst-name
                            "unnamed-staff")
                          ".notes"))))

Wenn Du die markierte Zeile betrachtest, so unterscheidet sie sich doch wesentlich von Deinem Code:
Zitat
(substring kommandozeile
  (+ (string-rindex kommandozeile #\sp) 2)
  (+ (string-rindex kommandozeile #\/) 1))

In Deinem Code wird nach #\/ gesucht.
Das geht nun mal baden falls im letzten Listeneintrag kein #\/ vorhanden ist.

Nichtsdestotrotz hätte ich es in event-listener.ly anders codiert.

Gruß,
  Harm

fugenkomponist

  • Member
Re: ly -> JSON ?
« Antwort #9 am: Sonntag, 6. März 2016, 17:38 »
Wenn ich das richtig verstehe, ist event-listener.ly eigentlich nur für ein spezielles Projekt namens „vivi“ (welches eigentlich mit LilyPond gar nichts zu tun hat, sondern nur vom gleichen Autor ist) in LilyPond enthalten, oder? Merkwürdig eigentlich, aber das könnte erklären, warum das vielleicht nicht optimal geschrieben ist – es musste vielleicht nur mit einem bestimmten System klarkommen ;)

Edit: es gibt aber einen regression test und LilyPond wird ja i. d. R. unter Linux entwickelt, also müsste es mit Linux definitiv klarkommen … Falls ich hier gerade in ne vollkommen falsche Richtung gehe: Ich hab den Thread ehrlich gesagt nur überflogen.
« Letzte Änderung: Sonntag, 6. März 2016, 17:42 von fugenkomponist »