Unfold musical jumps

10

The Dal Segno and Da Capo are two very commonly used musical terms. They mean "from the sign" () and "from the beginning" respectively.

There is also the idea of the coda (), which is the very end of a piece of music. It's what's played after the "main section" of the piece.

A D.S. al coda (Dal Segno al coda), for example, means "go to the segno, play until you're told to go to the coda, and then jump there."

Description

Your job in this challenge is to take input composed of any number of notes which may or may not contain Dal Segnos and Da Capos and output the same music with the aforementioned jumps "unfolded" so that the repetitions are expanded out verbatim.

Input

Your code should take as input a sequence of either notes or signals (here defined as anything but a note), separated by spaces in one string.

  • Notes are any of a, b, c, d, e, f, or g, with an optional # or b appended (for the purposes of this challenge, there is no rhythm).

  • A C (capital c) represents a coda marking. There will always be either zero or two coda markings; the first coda marking represents where to jump from, and the second represents where to jump to.

  • An S (capital s) represents a signo marking. There will always be either zero or one signo marking(s).

  • An F (capital f) represents a fine marking. This "overrides" the end of the piece—more on that below. There will always be either zero or one fine marking(s).

  • Any of the following exact strings of text represent:

    • D.S. al fine: go to the signo and play until either the end of the piece or the fine marking (if it exists).

    • D.S. al coda: go to the signo, play until the coda, then jump to the second coda marking and play until the end of the piece.

    • D.C. al fine: go to the beginning, play until the end or the fine marking.

    • D.C. al coda: go to the beginning, play until the coda, then jump to the second coda marking and play until the end of the piece.

    There will always be a minimum of zero and a maximum of one of each string per piece. There will never be multiple al fines or multiple al codas in a piece.

Output

Your code should output in a similar string format: a list of notes, separated by spaces.

You may always assume that the output will end up being one or more characters long.

Test cases

In: a# bb c b a
Out: a# bb c b a

In: a S b D.S. al fine c
Out: a b b c

In: a S b C c D.S. al coda d C e
Out: a b c b e

In: a b F c d D.C. al fine e f
Out: a b c d a b

In: a b D.C. al fine c d F e f
Out: a b a b c d

In: a b C c d D.C. al coda e f C g g#
Out: a b c d a b g g#

In: a b D.C. al coda c d C e f C g g#
Out: a b a b c d g g#

In: a b S c d C D.C. al coda C D.S. al fine e f F g
Out: a b c d a b c d c d e f

In: a S b C c D.S. al coda C d D.S. al fine e F f
Out: a b c b d b c d e

In: a b C c d D.C. al coda e f F g g# C gb a# D.C. al fine
Out: a b c d a b gb a# a b c d e f

In: a F b C D.C. al coda C D.C. al fine
Out: a b a b a

In: C a S b D.C. al coda C c D.S. al fine d
Out: a b c b c d

In: a S b D.S. al coda C C c D.C. al fine
Out: a b b c a b c

In: a F C b C D.C. al coda D.C. al fine
Out: a b a a

Rules

  • The markings will always appear in a logical order. That is, there will never be an S after a D.S. and there will always be one before, etc.

  • This is , so the shortest code in bytes will win.

Doorknob

Posted 2015-12-07T12:33:44.293

Reputation: 68 138

Answers

1

JavaScript (ES6), 253 bytes

x=>eval('n=(" "+x).replace(/D.{11}|[CSF]/g,d=>({C:4,S:5,F:6}[d]|(d[2]<"S")*2+(d[8]<"f"))).split` `;for(i=c=f=o="";v=n[++i];v<9?v<4?(n[i]=7,i=0,s=n.indexOf`5`,v==0?f=i=s:v==1?c=i=s:v==2?f=1:c=1):v==4&c?c=!(i=n.indexOf("4",i+1)):v==6&f?i=n:0:o+=v+" ");o')

Explanation

Could be golfed better but I'm done for now.

x=>
  eval(`                                  // use eval to enable for loop without return
    n=(" "+x)                             // n = array of [ "", ...notes/commands ]
                                          // empty first element means f and c can be set
                                          //     to i (always true) in the cases below
      // DS fine => 0, DS coda => 1, DC fine => 2, DC coda => 3, C => 4, S => 5, F => 6
      .replace(/D.{11}|[CSF]/g,d=>({C:4,S:5,F:6}[d]|(d[2]<"S")*2+(d[8]<"f")))
      .split\` \`;
    for(
      i=                                  // i = position in note array
      c=                                  // c = look out for coda if true
      f=                                  // f = look out for fine if true
      o="";                               // o = output string
      v=n[++i];                           // v = note/command
      v<9?                                // if not a note
        v<4?(                             // if DS/DC
          n[i]=7,                         // change it to NOP
          i=0,                            // reset i here to save doing it in DC cases
          s=n.indexOf\`5\`,
          v==0?f=i=s:                     // case: D.S. al fine
          v==1?c=i=s:                     // case: D.S. al coda
          v==2?f=1:                       // case: D.C. al fine
          c=1                             // case: D.C. al coda
        ):
        v==4&c?c=!(i=n.indexOf("4",i+1)): // case: C
        v==6&f?i=n:                       // case: F
        0                                 // case: S
      :o+=v+" "                           // add the note
    );o                                   // return the output
  `)

Test

var solution = x=>eval('n=(" "+x).replace(/D.{11}|[CSF]/g,d=>({C:4,S:5,F:6}[d]|(d[2]<"S")*2+(d[8]<"f"))).split` `;for(i=c=f=o="";v=n[++i];v<9?v<4?(n[i]=7,i=0,s=n.indexOf`5`,v==0?f=i=s:v==1?c=i=s:v==2?f=1:c=1):v==4&c?c=!(i=n.indexOf("4",i+1)):v==6&f?i=n:0:o+=v+" ");o')
<input type="text" id="input" value="a b C c d D.C. al coda e f F g g# C gb a# D.C. al fine" />
<button onclick="result.textContent=solution(input.value)">Go</button>
<pre id="result"></pre>

user81655

Posted 2015-12-07T12:33:44.293

Reputation: 10 181