Пример #1
0
// ParseTextForChords parses a line of text for chords and their positions. It returns true if
// this was just a line of chords and false if it contains text other than chords.
func parseTextForChords(text string) ([]*songtools.Chord, []int, bool) {

	chords := []*songtools.Chord{}
	positions := []int{}

	i := 0
	for i < len(text) {
		for i < len(text) && text[i] == ' ' {
			i++
		}

		if i == len(text) {
			break
		}

		name := ""
		pos := i
		for i < len(text) && text[i] != ' ' {
			name += string(text[i])
			i++
		}

		chord, ok := songtools.ParseChord(name)
		if !ok {
			// we aren't a chord line
			return nil, nil, false
		}

		chords = append(chords, chord)
		positions = append(positions, pos)
	}

	return chords, positions, len(chords) > 0
}
Пример #2
0
func (p *parser) parseSong() (*songtools.Song, error) {

	song := &songtools.Song{}

	token, text, err := p.scanner.next()
	if err != nil {
		return nil, err
	}

	var section *songtools.Section
	var line *songtools.Line
	numNewLines := 0

	for token != eofToken {
		switch token {
		case commentToken:
			comment := &songtools.Comment{
				Text:   text,
				Hidden: true,
			}
			if section != nil {
				section.Nodes = append(section.Nodes, comment)
			} else {
				song.Nodes = append(song.Nodes, comment)
			}
		case directiveToken:
			d, err := parseDirective(text)
			if err != nil {
				return nil, err
			}

			switch d.Name {
			case startOfChorusDirectiveName:
				section = &songtools.Section{
					Kind: songtools.SectionKind("Chorus"),
				}
				song.Nodes = append(song.Nodes, section)
			case endOfChorusDirectiveName:
				section = nil
			case startOfBridgeDirectiveName:
				section = &songtools.Section{
					Kind: songtools.SectionKind("Bridge"),
				}
				song.Nodes = append(song.Nodes, section)
			case endOfBridgeDirectiveName:
				section = nil
			case commentDirectiveName:

				if section == nil {
					la := 1
					for {
						// we are going to look forward past all the comments until we find a non-comment
						nextToken, _, nextErr := p.scanner.la(la)
						if nextErr != nil {
							break
						}
						if nextToken == directiveToken {
							innerD, err := parseDirective(text)
							if err != nil {
								break
							}
							if innerD.Name != commentDirectiveName {
								break
							}
							la += 2
						} else if nextToken == chordToken || nextToken == textToken {
							section = &songtools.Section{
								Kind: songtools.SectionKind(d.Value),
							}
							song.Nodes = append(song.Nodes, section)
							break
						} else {
							break
						}
					}
				} else {
					comment := &songtools.Comment{
						Text:   d.Value,
						Hidden: false,
					}
					if section == nil {
						song.Nodes = append(song.Nodes, comment)
					} else {
						section.Nodes = append(section.Nodes, comment)
					}
				}
			case titleDirectiveName:
				song.Title = d.Value
			case subtitleDirectiveName:
				song.Subtitles = append(song.Subtitles, d.Value)
			case authorDirectiveName:
				song.Authors = append(song.Authors, d.Value)
			case keyDirectiveName:
				song.Key = songtools.Key(d.Value)
			default:
				// choruses and bridges have end tags, which means we can just wait until those show up
				// and not have to guess at the end of a section.
				if section != nil && numNewLines == 2 && section.Kind != "Chorus" && section.Kind != "Bridge" {
					section = nil
				}

				if section != nil {
					section.Nodes = append(section.Nodes, d)
				} else {
					song.Nodes = append(song.Nodes, d)
				}
				line = nil
				numNewLines = 0
			}
		case chordToken:
			if section == nil {
				section = &songtools.Section{}
				song.Nodes = append(song.Nodes, section)
			}

			if line == nil {
				line = &songtools.Line{}
				section.Nodes = append(section.Nodes, line)
			}

			chord, ok := songtools.ParseChord(text)
			if !ok {
				return nil, fmt.Errorf("The text '%v' is not a chord.", text)
			}

			line.Chords = append(line.Chords, chord)
			line.ChordPositions = append(line.ChordPositions, len(line.Text))

		case textToken:
			if section == nil {
				section = &songtools.Section{}
				song.Nodes = append(song.Nodes, section)
			}

			if line == nil {
				line = &songtools.Line{}
				section.Nodes = append(section.Nodes, line)
			}

			line.Text += text

			numNewLines = 0
		case newLineToken:
			line = nil
			numNewLines++
			if section != nil && numNewLines == 2 && section.Kind != "Chorus" && section.Kind != "Bridge" {
				section = nil
				numNewLines = 0
			}

			break
		}

		token, text, err = p.scanner.next()
		if err != nil {
			return nil, err
		}
	}

	return song, nil
}