コード例 #1
0
ファイル: parser.go プロジェクト: songtools/songtools
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
}
コード例 #2
0
ファイル: main.go プロジェクト: songtools/songtools
func (cmd *options) execute(args []string) error {

	if len(args) > 1 {
		return fmt.Errorf("too many positional arguments")
	}

	var err error
	in := os.Stdin
	file := ""
	if len(args) == 1 {
		file = args[0]
		in, err = os.Open(file)
		if err != nil {
			return fmt.Errorf("unable to open %q: %v", file, err)
		}
	}

	inBytes, err := ioutil.ReadAll(in)
	if err != nil {
		return fmt.Errorf("unable to read: %v", err)
	}

	input := bytes.NewBuffer(inBytes)

	readFormat, err := findReadFormat(cmd.CurrentFormat, file, input)
	if err != nil {
		return fmt.Errorf("unable to find input format for %q: %v", file, err)
	}

	writeFormat := readFormat
	if cmd.ToFormat != "" {
		var ok bool
		if writeFormat, ok = format.ByName(cmd.ToFormat); !ok {
			return fmt.Errorf("unable to find output format %q", cmd.ToFormat)
		}
	}

	if writeFormat.Writer == nil {
		return fmt.Errorf("the input format %q is unable to be used for writing", readFormat.Name)
	}

	song, err := readFormat.Reader.Read(input)
	if err != nil {
		return fmt.Errorf("unable to parse %q: %v", file, err)
	}

	setSongTitleIfNecessary(file, song)

	if cmd.ToKey != "" {
		fromKey := songtools.Key(cmd.CurrentKey)

		if fromKey == "" && song.Key == "" {
			return fmt.Errorf("unable to get current key")
		} else if fromKey == "" {
			fromKey = song.Key
		}

		toKey := songtools.Key(cmd.ToKey)

		noteNames, interval, err := songtools.NoteNamesAndIntervalFromKeyToKey(fromKey, toKey)
		if err != nil {
			return fmt.Errorf("unable to get note names and interval: %v", err)
		}

		song, err = songtools.TransposeSong(song, interval, noteNames)
		if err != nil {
			return fmt.Errorf("unable to transpose from %q to %q: %v", fromKey, toKey, err)
		}

		song.Key = toKey
	}

	out := os.Stdout
	defer out.Close()

	if cmd.Out == "<unset>" {
		name := song.Title
		if name == "" && file == "" {
			return fmt.Errorf("'out' was specified, but the song does not have a title and the input was not a file")
		} else if name == "" {
			_, file := filepath.Split(file)
			ext := filepath.Ext(file)
			name = strings.TrimSuffix(file, ext)
		}

		if len(writeFormat.Extensions) > 0 {
			name += writeFormat.Extensions[0]
		}

		cmd.Out = name
	}

	if cmd.Out != "" {
		out, err = os.OpenFile(cmd.Out, os.O_CREATE, 0666)
		if err != nil {
			return fmt.Errorf("unable to open %q: %v", cmd.Out, err)
		}
	}

	return writeFormat.Writer.Write(out, song)
}
コード例 #3
0
ファイル: parser.go プロジェクト: songtools/songtools
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: false,
			}
			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
			}

			if numNewLines == 2 {
				section = nil
			}

			if section != nil {
				section.Nodes = append(section.Nodes, d)
			} else {
				switch d.Name {
				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:
					song.Nodes = append(song.Nodes, d)
				}

			}
			line = nil
			numNewLines = 0
		case sectionToken:
			section = &songtools.Section{
				Kind: songtools.SectionKind(text),
			}

			song.Nodes = append(song.Nodes, section)
			line = nil
			numNewLines = 0
		case textToken:
			if section != nil && numNewLines == 0 {
				// we have text immediately following a section without a newline
				directive := &songtools.Comment{
					Text:   text,
					Hidden: false,
				}
				section.Nodes = append(section.Nodes, directive)
				break
			}

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

			chords, positions, isChordLine := parseTextForChords(text)
			if !isChordLine {
				if line != nil && line.Text == "" {
					line.Text = text
				} else {
					line = &songtools.Line{
						Text: text,
					}
					section.Nodes = append(section.Nodes, line)
				}
				line = nil
			} else {
				line = &songtools.Line{
					Chords:         chords,
					ChordPositions: positions,
				}
				section.Nodes = append(section.Nodes, line)
			}
			numNewLines = 0
		case newLineToken:
			numNewLines++
			if numNewLines > 2 {
				section = nil
				line = nil
				numNewLines = 0
			}
			break
		}

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

	return song, nil
}