Music and Parsers


Photo note -I actually did see a prepared piano performance at Roulette about a decade ago when it was down in Soho. It was amazing! Now on to business.

Here is my first chess to music via Euterpea composition. Its experimental  lets leave it at that…

The Backstory….

A few Recursors and I  planned to write a chess notation parser (pgn) in Haskell last week.  I have been doing the Haskell exercises from this git repo, and I’ve been reading up on the basics from an online book – like WTF is foldable (its not that complex – just something that is reducible to less elements  like a tree or list). I could do some sort of brut force parser, but I wanted to learn something so I broke out my dragon book and dove in!

At this point it became evident that perhaps this project was less about Haskell and more about compilers. As it turned out I ended up not using Haskell at all,  but used Lex/Yacc  – which has fulfilled a decade(s) old wish.

What would a compiler look like if the language was pgn (chess notation) and we were going to compile it to musical notation to be executed by the Haskell library euterpea?What if we played a chess game as a musical composition?

This has been interesting to me for a long time since the 8×8 matrix of the chess board maps to an octave scale.  It is also interesting to me because you could also say that the rook, knight, bishop, king, queen map to the pentatonic scale.   It is also interesting to me because I am interested in transduction taking energy from one system and using it in another, so the energy in chess as powering a musical composition. It brings up for me all sorts of questions like what is the best mapping for this? Is there a best mapping. What will this sound like?  And so forth.

download (3)

Back to compilers! Generally, we think of a compiler as taking some source code and translating into machine code.   This goes like

  1. preprocessor – generate all code from macros and what not
  2. compile – translate the code to assembly
  3. assembler – translate the assembly to binary
  4. linker – link the libraries and other code

I am only interested in the compile piece  at this point – to turn one language into another (and not assembly in this case, but a protocol readable by Euterpea.  Although it is interesting to think about the other pieces. And in the case of compiler or code optimization we do want to look at the machine code or byte code or linking. (And maybe turn that into music too who knows)

To focus on the compiler -what I am interested in doing is

  1. creating a lexical analysis – that is a list of all the tokens in the language
  2. write  grammar that describes the language
  3. creating a parser – that builds the expression tree of how the tokens work together
  4. generate/emit the euterpea code/protocol

Lets get granular. “Here is the PGN format of the 29th game of the 1992 match played in Yugoslavia between Bobby Fischer and Boris Spassky” I took from wikipedia.

[Event "F/S Return Match"]
[Site "Belgrade, Serbia JUG"]
[Date "1992.11.04"]
[Round "29"]
[White "Fischer, Robert J."]
[Black "Spassky, Boris V."]
[Result "1/2-1/2"]

1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 {This opening is called the Ruy Lopez.}
4. Ba4 Nf6 5. O-O Be7 6. Re1 b5 7. Bb3 d6 8. c3 O-O 9. h3 Nb8 10. d4 Nbd7
11. c4 c6 12. cxb5 axb5 13. Nc3 Bb7 14. Bg5 b4 15. Nb1 h6 16. Bh4 c5 17. dxe5
Nxe4 18. Bxe7 Qxe7 19. exd6 Qf6 20. Nbd2 Nxd6 21. Nc4 Nxc4 22. Bxc4 Nb6
23. Ne5 Rae8 24. Bxf7+ Rxf7 25. Nxf7 Rxe1+ 26. Qxe1 Kxf7 27. Qe3 Qg5 28. Qxg5
hxg5 29. b3 Ke6 30. a3 Kd6 31. axb4 cxb4 32. Ra5 Nd5 33. f3 Bc8 34. Kf2 Bf5
35. Ra7 g6 36. Ra6+ Kc5 37. Ke1 Nf4 38. g3 Nxh3 39. Kd2 Kb5 40. Rd6 Kc5 41. Ra6
Nf2 42. g4 Bd3 43. Re6 1/2-1/2

First I start with the lexical analysis.  This is what decides what and is what is not a token. I am going to throw out everything above the 1. So the game metadata really. I may want to change this in the future – but that is it.  I am also ignoring everything in {} again more metadata, and I am ignoring all spaces. I will keep tokens as Num. and the number 1-8,  abcdefghNBRKQ and /.

Next I attend to the parser or syntactical analysis. There are a bunch of parsers, and parser methodologies I could implement if I end up doing a Haskell implementation but for now I am using Lex/Yacc – which uses an LF parser.  Lex parses the tokens – it is like a bunch of regular expressions. Yacc defines the grammar and the emitter – which is fun because who does not love Backus-Naur Form??

The Lex/Yacc is not close to fully functional. The following  resources have been helpful. Sadly, I experienced a type error stripped all functionality out in an attempt to debug. Let this be a lesson. Commit early and often!

I should do some smart contract programming so I am just going to leave it half baked on github.  I have been playing around with some possible code generators (emitters) for Euterpea.   We can call this semantic analysis maybe (or maybe not). This is where we generate the musical notation for Euterpea. I had a great time doing this. I recorded the playback via quicktime audio recording because the code for saving Euterpea to wav was onerous. However, I think the background noise does add to the ambience. I just need to bump up the gain.

After this experiment, I learned that I should rethink the pitch mapping.  I would be curious to hear another chess game run through this. Will they all sound the same? If this is the case then my mapping definitely needs reworking!

Dipping my toe into compilers after decade(s) has been fun. I want to continue this discussion. Especially building the compiler in Haskell (not lex/yacc, building a recognizer in the lexical analysis (determining whether or not the input is in the language), parser deep dive, a symbol table deep dive, and of course, my favorite, code generation. But for now I am just happy to use the unix glue commands to generate a parser for pgn.

Leave a Reply