gly 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/bin/gly +11 -0
  3. data/bin/glyfy +4 -0
  4. data/lib/gly.rb +5 -0
  5. data/lib/gly.rb~ +5 -0
  6. data/lib/gly/cli.rb +94 -0
  7. data/lib/gly/cli.rb~ +13 -0
  8. data/lib/gly/document.rb +15 -0
  9. data/lib/gly/document.rb~ +8 -0
  10. data/lib/gly/gabc_convertor.rb +60 -0
  11. data/lib/gly/gabc_convertor.rb~ +5 -0
  12. data/lib/gly/headers.rb +80 -0
  13. data/lib/gly/headers.rb~ +13 -0
  14. data/lib/gly/lyrics.rb +39 -0
  15. data/lib/gly/lyrics.rb~ +21 -0
  16. data/lib/gly/parsed_score.rb +16 -0
  17. data/lib/gly/parsed_score.rb~ +6 -0
  18. data/lib/gly/parser.rb +106 -0
  19. data/lib/gly/parser.rb~ +6 -0
  20. data/lib/gly/string_helpers.rb +34 -0
  21. data/lib/gly/string_helpers.rb~ +36 -0
  22. data/tests/examples.rb +28 -0
  23. data/tests/examples.rb~ +9 -0
  24. data/tests/examples/gly/expected/block_lyrics.gabc +2 -0
  25. data/tests/examples/gly/expected/differentiae.gabc +2 -0
  26. data/tests/examples/gly/expected/differentiae.gabc~ +2 -0
  27. data/tests/examples/gly/expected/document_header.gabc +4 -0
  28. data/tests/examples/gly/expected/document_header.gabc~ +4 -0
  29. data/tests/examples/gly/expected/empty.gabc +1 -0
  30. data/tests/examples/gly/expected/explicit_lyrics.gabc +2 -0
  31. data/tests/examples/gly/expected/explicit_lyrics.gabc~ +2 -0
  32. data/tests/examples/gly/expected/explicit_score.gabc +2 -0
  33. data/tests/examples/gly/expected/force_divisio_lyrics.gabc +2 -0
  34. data/tests/examples/gly/expected/header.gabc +3 -0
  35. data/tests/examples/gly/expected/header_unofficial.gabc +4 -0
  36. data/tests/examples/gly/expected/header_unofficial.gabc~ +3 -0
  37. data/tests/examples/gly/expected/multiline.gabc +2 -0
  38. data/tests/examples/gly/expected/multiline_interlaced.gabc +2 -0
  39. data/tests/examples/gly/expected/nonlyrical_lyrics.gabc +2 -0
  40. data/tests/examples/gly/expected/nonlyrical_lyrics.gabc~ +2 -0
  41. data/tests/examples/gly/expected/notes_bracketted.gabc +2 -0
  42. data/tests/examples/gly/expected/notes_bracketted.gabc~ +1 -0
  43. data/tests/examples/gly/expected/notes_only.gabc +2 -0
  44. data/tests/examples/gly/expected/notes_with_lyrics.gabc +2 -0
  45. data/tests/examples/gly/expected/notes_with_lyrics.gabc~ +2 -0
  46. data/tests/examples/gly/expected/unsyllabified_lyrics.gabc +2 -0
  47. data/tests/examples/gly/given/block_lyrics.gly +3 -0
  48. data/tests/examples/gly/given/differentiae.gly +3 -0
  49. data/tests/examples/gly/given/differentiae.gly~ +3 -0
  50. data/tests/examples/gly/given/document_header.gly +9 -0
  51. data/tests/examples/gly/given/empty.gly +0 -0
  52. data/tests/examples/gly/given/explicit_lyrics.gly +2 -0
  53. data/tests/examples/gly/given/explicit_score.gly +2 -0
  54. data/tests/examples/gly/given/force_divisio_lyrics.gly +2 -0
  55. data/tests/examples/gly/given/header.gly +2 -0
  56. data/tests/examples/gly/given/header_unofficial.gly +3 -0
  57. data/tests/examples/gly/given/header_unofficial.gly~ +2 -0
  58. data/tests/examples/gly/given/multiline.gly +4 -0
  59. data/tests/examples/gly/given/multiline_interlaced.gly +5 -0
  60. data/tests/examples/gly/given/multiline_interlaced.gly~ +4 -0
  61. data/tests/examples/gly/given/nonlyrical_lyrics.gly +2 -0
  62. data/tests/examples/gly/given/nonlyrical_lyrics.gly~ +2 -0
  63. data/tests/examples/gly/given/notes_bracketted.gabc~ +1 -0
  64. data/tests/examples/gly/given/notes_bracketted.gly +1 -0
  65. data/tests/examples/gly/given/notes_only.gly +1 -0
  66. data/tests/examples/gly/given/notes_with_lyrics.gly +2 -0
  67. data/tests/examples/gly/given/unsyllabified_lyrics.gly +2 -0
  68. data/tests/examples/gly/no_crash/document_header.gly~ +4 -0
  69. data/tests/examples/gly/no_crash/multiple_scores.gly +10 -0
  70. data/tests/examples/gly/no_crash/multiple_scores.gly~ +7 -0
  71. data/tests/no_crash.rb +20 -0
  72. data/tests/programmed_examples.rb~ +14 -0
  73. data/tests/run.rb +3 -0
  74. data/tests/run.rb~ +3 -0
  75. data/tests/string_helpers_test.rb +19 -0
  76. data/tests/string_helpers_test.rb~ +9 -0
  77. data/tests/test_helper.rb +17 -0
  78. metadata +193 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 341817505f45deff8e6c9815061fa6cffa571858
4
+ data.tar.gz: 64ff6cb093190e69c37ed874af5c9b34e1499c3f
5
+ SHA512:
6
+ metadata.gz: 41ba343096d37f095b7e9ca1724a8692d7bf4ca6dfcac689e681e9c61c1c95067ec42ba18f6fa8de259f6bf70b1960e5108f1f2503be1044e8df64bb0ec6fa96
7
+ data.tar.gz: a14a88215832854321ecadff5fd0f0c01a057d3ad991873aede7d91b879da35edfe197bca214144a55a4ad4ff893d0a365505849ce62020b9da7361a5fe9a2e8
data/bin/gly ADDED
@@ -0,0 +1,11 @@
1
+ #!/bin/env ruby
2
+
3
+ # gly
4
+ # takes gly file, produces gabc file(s);
5
+ # optionally makes a simple tex file including it(them) and
6
+ # builds a pdf.
7
+
8
+ require 'gly'
9
+ require 'gly/cli'
10
+
11
+ Gly::CLI.start ARGV
@@ -0,0 +1,4 @@
1
+ #!/bin/env ruby
2
+
3
+ # glyfy
4
+ # takes gabc file(s), produces gly file
@@ -0,0 +1,5 @@
1
+ module Gly; end
2
+
3
+ %w(parser gabc_convertor parsed_score headers lyrics document string_helpers).each do |mod|
4
+ require "gly/#{mod}"
5
+ end
@@ -0,0 +1,5 @@
1
+ module Gly; end
2
+
3
+ %w(parser gabc_convertor).each do |mod|
4
+ require "lyv/#{mod}"
5
+ end
@@ -0,0 +1,94 @@
1
+ require 'thor'
2
+
3
+ module Gly
4
+ # implements the 'gly' executable
5
+ class CLI < Thor
6
+ desc 'gabc FILE ...', 'convert gly to gabc'
7
+ def gabc(*files)
8
+ files.each {|f| gabc_convert(parse(f)) }
9
+ end
10
+
11
+ desc 'preview FILE ...', 'convert to gabc AND generate pdf preview'
12
+ def preview(*files)
13
+ files.each {|f| make_preview f }
14
+ end
15
+
16
+ private
17
+
18
+ def parse(gly_file)
19
+ document = File.open(gly_file) do |fr|
20
+ Parser.new.parse(fr)
21
+ end
22
+ end
23
+
24
+ def gabc_convert(doc)
25
+ doc.scores.each_with_index do |score, si|
26
+ score_id = score.headers['id'] || si.to_s
27
+ out_fname = File.basename(doc.path)
28
+ .sub(/\.gly\Z/i, "_#{score_id}.gabc")
29
+ File.open(out_fname, 'w') do |fw|
30
+ GabcConvertor.new.convert score, fw
31
+ end
32
+ yield score, out_fname if block_given?
33
+ end
34
+ end
35
+
36
+ def make_preview(gly_file)
37
+ doc = parse(gly_file)
38
+
39
+ tex_header = <<EOS
40
+ % LuaLaTeX
41
+
42
+ \\documentclass[a4paper, 12pt]{article}
43
+ \\usepackage[latin]{babel}
44
+ \\usepackage[left=2cm, right=2cm, top=2cm, bottom=2cm]{geometry}
45
+
46
+ \\usepackage{fontspec}
47
+
48
+ % for gregorio
49
+ \\usepackage{luatextra}
50
+ \\usepackage{graphicx}
51
+ \\usepackage{gregoriotex}
52
+
53
+ \\title{#{doc.header['title']}}
54
+
55
+ \\begin{document}
56
+
57
+ #{doc.header['title'] && '\\maketitle'}
58
+
59
+ EOS
60
+
61
+ tex_fname = File.basename(gly_file).sub(/\.gly\Z/i, '.tex')
62
+ File.open(tex_fname, 'w') do |fw|
63
+ fw.puts tex_header
64
+
65
+ gabc_convert(doc) do |score, gabc_fname|
66
+ system "gregorio #{gabc_fname}"
67
+ gtex_fname = gabc_fname.sub /\.gabc/i, ''
68
+ piece_title = %w(book manuscript arranger author).collect do |m|
69
+ score.headers[m]
70
+ end.delete_if(&:nil?).join ', '
71
+ fw.puts "\\commentary{\\footnotesize{#{piece_title}}}\n" unless piece_title.empty?
72
+
73
+ annotations = score.headers.each_value('annotation')
74
+ begin
75
+ fw.puts "\\setfirstannotation{#{annotations.next}}"
76
+ fw.puts "\\setsecondannotation{#{annotations.next}}"
77
+ rescue StopIteration
78
+ # ok, no more annotations
79
+ end
80
+
81
+ fw.puts "\\includescore{#{gtex_fname}}\n\\vspace{1cm}"
82
+ end
83
+
84
+ # tagline
85
+ fw.puts "\n\\vfill\n\\begin{center}"
86
+ fw.puts "\\texttt{gly preview https://github.com/igneus/gly}"
87
+ fw.puts "\\end{center}\n"
88
+ fw.puts "\n\\end{document}"
89
+ end
90
+
91
+ system "lualatex #{tex_fname}"
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,13 @@
1
+ module Gly
2
+ class CLI < Thor
3
+ desc 'gabc FILE ...', 'convert gly to gabc'
4
+ def gabc(*files)
5
+
6
+ end
7
+
8
+ desc 'preview FILE ...', 'convert to gabc AND generate pdf preview'
9
+ def preview(*files)
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module Gly
2
+ # Result of parsing of a gly file.
3
+ # Usually contains one or more scores
4
+ # and possibly a document header.
5
+ class Document
6
+ def initialize
7
+ @scores = []
8
+ @header = Headers.new
9
+ @path = nil
10
+ end
11
+
12
+ attr_reader :scores, :header
13
+ attr_accessor :path
14
+ end
15
+ end
@@ -0,0 +1,8 @@
1
+ module Gly
2
+ # Result of parsing of a gly file.
3
+ # Usually contains one or more scores
4
+ # and possibly a document header.
5
+ class Document
6
+
7
+ end
8
+ end
@@ -0,0 +1,60 @@
1
+ module Gly
2
+ # converts parsed gly to gabc
3
+ class GabcConvertor
4
+ def convert(score, out=StringIO.new)
5
+ score.headers.each_pair do |key,value|
6
+ out.print '% ' unless Headers.gregorio_supported?(key)
7
+ out.puts "#{key}: #{value};"
8
+ end
9
+
10
+ out.puts '%%'
11
+
12
+ lyric_enum = score.lyrics.each_syllable.to_enum
13
+ score.music.each_with_index do |mus_chunk,i|
14
+ begin
15
+ next_syl = lyric_enum.peek
16
+ rescue StopIteration
17
+ next_syl = ''
18
+ end
19
+
20
+ if clef?(mus_chunk) ||
21
+ (nonlyrical_chunk?(mus_chunk) && ! nonlyrical_lyrics?(next_syl))
22
+ # music chunk normally not having lyrics
23
+ out.print ' ' if i != 0
24
+ else
25
+ # regular music chunk
26
+ begin
27
+ out.print strip_directives lyric_enum.next
28
+ rescue StopIteration
29
+ out.print ' ' if i != 0
30
+ end
31
+ end
32
+ out.print "(#{mus_chunk})"
33
+ # out.puts if differentia?(mus_chunk) # newline after each differentia
34
+ end
35
+
36
+ return out
37
+ end
38
+
39
+ def clef?(chunk)
40
+ chunk =~ /\A[cf][1-4]\Z/
41
+ end
42
+
43
+ def differentia?(chunk)
44
+ chunk =~ /\A*[,;:]+\Z/ # differentia
45
+ end
46
+
47
+ # is the given music chunk capable of bearing lyrics?
48
+ def nonlyrical_chunk?(chunk)
49
+ differentia? chunk
50
+ end
51
+
52
+ def nonlyrical_lyrics?(syl)
53
+ syl =~ /\A\s*!/ || syl =~ /\A\s*\*\Z/
54
+ end
55
+
56
+ def strip_directives(syl)
57
+ syl.sub(/(\s*)!/, '\1') # exclamation mark at the beginning - place even under nonlyrical music chunk
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ module Gly
2
+ # converts parsed gly to gabc
3
+ class GabcConvertor
4
+ end
5
+ end
@@ -0,0 +1,80 @@
1
+ require 'forwardable'
2
+
3
+ module Gly
4
+ # score or document header
5
+ class Headers
6
+ extend Forwardable
7
+
8
+ # header fields supported by gregorio.
9
+ # Based on http://gregorio-project.github.io/gabc/index.html#header
10
+ # last checked 2015-12-06
11
+ GREGORIO_HEADERS = %w(
12
+ name
13
+ gabc-copyright
14
+ score-copyright
15
+ office-part
16
+ occasion
17
+ meter
18
+ commentary
19
+ arranger
20
+ gabc-version
21
+ author
22
+ date
23
+ manuscript
24
+ manuscript-reference
25
+ manuscript-storage-place
26
+ book
27
+ transcriber
28
+ transcription-date
29
+ gregoriotex-font
30
+ mode
31
+ initial-style
32
+ centering-scheme
33
+ user-notes
34
+ annotation
35
+ )
36
+
37
+ def initialize
38
+ # for quick lookup. Only holds a single value per header
39
+ @headers = {}
40
+ # holds order and as many values for a header as given in the
41
+ # score (typically annotation occurs more than once)
42
+ @pairs = []
43
+ end
44
+
45
+ def []=(key, value)
46
+ @headers[key] = value
47
+ @pairs << [key, value]
48
+ end
49
+
50
+ def_delegator :@headers, :[]
51
+ def_delegator :@pairs, :empty?
52
+
53
+ # some header fields may appear more than once;
54
+ # this method provides access to values of all occurrences
55
+ # of a given header field
56
+ def each_value(key)
57
+ return to_enum(:each_value, key) unless block_given?
58
+
59
+ each_pair do |k,v|
60
+ yield v if k == key
61
+ end
62
+ end
63
+
64
+ def each_pair
65
+ return to_enum(:each_pair) unless block_given?
66
+
67
+ @pairs.each do |k|
68
+ yield k[0], k[1]
69
+ end
70
+ end
71
+
72
+ def self.gregorio_supported?(key)
73
+ GREGORIO_HEADERS.include? key
74
+ end
75
+
76
+ def headers
77
+ self
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,13 @@
1
+ module Gly
2
+ class Headers
3
+ def initialize
4
+ @headers = {}
5
+ @keys_order = []
6
+ end
7
+
8
+ def []=(key, value)
9
+ @headers[key] = value
10
+ @keys_order << key
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,39 @@
1
+ require 'forwardable'
2
+
3
+ module Gly
4
+ class Lyrics
5
+ extend Forwardable
6
+
7
+ def initialize
8
+ @words = []
9
+ end
10
+
11
+ def each_syllable
12
+ return enum_for(:each_syllable) unless block_given?
13
+
14
+ @words.each do |w|
15
+ w.each_syllable.each_with_index do |s,si|
16
+ if si == 0
17
+ yield ' ' + s
18
+ else
19
+ yield s
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ def_delegator :@words, :each, :each_word
26
+ def_delegators :@words, :empty?, :<<
27
+ end
28
+
29
+ class Word
30
+ extend Forwardable
31
+
32
+ def initialize(syllables=[])
33
+ @syllables = syllables
34
+ end
35
+
36
+ def_delegators :@syllables, :<<, :push
37
+ def_delegator :@syllables, :each, :each_syllable
38
+ end
39
+ end
@@ -0,0 +1,21 @@
1
+ require 'forwardable'
2
+
3
+ module Gly
4
+ class Lyrics
5
+ def initialize
6
+ @words = []
7
+ end
8
+
9
+ def_delegator :@words, :each
10
+ alias_method :each_word, :each
11
+ end
12
+
13
+ class Word
14
+ def initialize
15
+ @syllables = []
16
+ end
17
+
18
+ def_delegator :@syllables, :<<, :push, :each
19
+ alias_method :each_syllable, :each
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ module Gly
2
+ # result of a gly score parsing
3
+ class ParsedScore
4
+ def initialize
5
+ @headers = Headers.new
6
+ @lyrics = Lyrics.new
7
+ @music = []
8
+ end
9
+
10
+ attr_reader :headers, :lyrics, :music
11
+
12
+ def empty?
13
+ @headers.empty? && @lyrics.empty? && @music.empty?
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,6 @@
1
+ # result of a gly score parsing
2
+ module Gly
3
+ class ParsedScore
4
+ attr_reader :headers, :lyrics, :notes
5
+ end
6
+ end
@@ -0,0 +1,106 @@
1
+ module Gly
2
+ # parses gly source
3
+ class Parser
4
+ SYLLABLE_SEP = '--'
5
+
6
+ def parse(io)
7
+ @doc = Document.new
8
+ @score = ParsedScore.new
9
+
10
+ if io.respond_to? :path
11
+ @doc.path = io.path
12
+ end
13
+
14
+ io.each do |line|
15
+ line = strip_comments(line)
16
+
17
+ if empty? line
18
+ next
19
+ elsif new_score? line
20
+ push_score
21
+ @score = ParsedScore.new
22
+ elsif header_start? line
23
+ push_score
24
+ @score = @doc.header
25
+ elsif header_line? line
26
+ parse_header line
27
+ elsif lyrics_line? line
28
+ parse_lyrics line
29
+ else
30
+ parse_music line
31
+ end
32
+ end
33
+
34
+ push_score
35
+
36
+ return @doc
37
+ end
38
+
39
+ private
40
+
41
+ def empty?(str)
42
+ str =~ /\A\s*\Z/
43
+ end
44
+
45
+ def new_score?(str)
46
+ str =~ /\A\s*\\score/
47
+ end
48
+
49
+ def header_start?(str)
50
+ str =~ /\A\s*\\header/
51
+ end
52
+
53
+ def strip_comments(str)
54
+ str.sub(/%.*\Z/, '')
55
+ end
56
+
57
+ def header_line?(str)
58
+ in_header_block? || @score.lyrics.empty? && @score.music.empty? && str =~ /\w+:\s*./
59
+ end
60
+
61
+ EXPLICIT_LYRICS_RE = /\A\\l(yrics)?\s+/
62
+
63
+ def lyrics_line?(str)
64
+ str =~ EXPLICIT_LYRICS_RE || str.include?(SYLLABLE_SEP) || contains_unmusical_letters?(str)
65
+ end
66
+
67
+ def in_header_block?
68
+ @score.is_a? Headers
69
+ end
70
+
71
+ def contains_unmusical_letters?(str)
72
+ letters = str.gsub(/[\W\d_]+/, '')
73
+ letters !~ /\A[a-mvwoxz]*\Z/i # incomplete gabc music letters!
74
+ end
75
+
76
+ def parse_header(str)
77
+ hid, hvalue = str.split(':').collect(&:strip)
78
+ @score.headers[hid] = hvalue
79
+ end
80
+
81
+ def parse_lyrics(str)
82
+ # words: split by whitespace not being part of syllable
83
+ # separator
84
+ str
85
+ .sub(EXPLICIT_LYRICS_RE, '')
86
+ .split(/(?<!#{SYLLABLE_SEP})\s+(?!#{SYLLABLE_SEP})/)
87
+ .each do |word|
88
+ @score.lyrics << Word.new(word.split(/\s*#{SYLLABLE_SEP}\s*/))
89
+ end
90
+ end
91
+
92
+ def parse_music(str)
93
+ # music chunks: split by whitespace out of brackets
94
+ str.split(/\s+/).each do |chunk|
95
+ chunk.sub!(/\A\((.*?)\)\Z/, '\1') # unparenthesize
96
+ @score.music << chunk
97
+ end
98
+ end
99
+
100
+ def push_score
101
+ if @score.is_a?(ParsedScore) && !@score.empty?
102
+ @doc.scores << @score
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,6 @@
1
+ module Gly
2
+ # parses gly source
3
+ class Parser
4
+
5
+ end
6
+ end
@@ -0,0 +1,34 @@
1
+ module Gly
2
+ module StringHelpers
3
+ extend self
4
+
5
+ # splits string by whitespace that is not enclosed
6
+ # in brackets.
7
+ # At the same time removes brackets.
8
+ def bracket_aware_whitespace_split(str)
9
+ str = str.strip
10
+ chunk_start = 0
11
+ chunks = []
12
+ in_brackets = false
13
+ str.chars.each_with_index do |char, chi|
14
+ if in_brackets
15
+ next if char != ')'
16
+
17
+ else
18
+ if char =~ /^\s$/
19
+ if chunk_start != chi
20
+ chunks << str[chunk_start .. chi-1]
21
+ end
22
+ chunk_start = chi+1
23
+ end
24
+ end
25
+ end
26
+
27
+ chunks << str[chunk_start .. -1]
28
+
29
+ chunks
30
+ end
31
+
32
+ alias_method :music_split, :bracket_aware_whitespace_split
33
+ end
34
+ end
@@ -0,0 +1,36 @@
1
+ module Gly
2
+ module StringHelpers
3
+ extend self
4
+
5
+ # splits string by whitespace that is not enclosed
6
+ # in brackets.
7
+ # At the same time removes brackets.
8
+ def bracket_aware_whitespace_split(str)
9
+ str = str.strip
10
+ chunk_start = 0
11
+ chunks = []
12
+ in_brackets = false
13
+ str.chars.each_with_index do |char, chi|
14
+ if in_brackets
15
+ next if char != ')'
16
+
17
+ else
18
+ if char =~ /^\s$/
19
+ if chunk_start != chi
20
+ chunks << str[chunk_start .. chi-1]
21
+ end
22
+ chunk_start = chi+1
23
+ end
24
+ end
25
+ end
26
+
27
+ if chunk_start != chi
28
+ chunks << str[chunk_start .. chi-1]
29
+ end
30
+
31
+ chunks
32
+ end
33
+
34
+ alias_method :music_split, :bracket_aware_whitespace_split
35
+ end
36
+ end
@@ -0,0 +1,28 @@
1
+ # test-suite dynamically created from examples in subfolders
2
+
3
+ require_relative 'test_helper'
4
+
5
+ # Each test case is defined by a pair of files
6
+ # in directories examples/given and examples/expected
7
+ # which define what is an expected gabc result of a single-score
8
+ # gly file.
9
+ class TestExamples < GlyTest
10
+ def self.example_test_case(given_file, expected_file)
11
+ # filename without extension
12
+ case_name = File.basename(given_file).sub(/\.[^\.]*\Z/, '')
13
+
14
+ define_method "test_#{case_name}" do
15
+ expected = File.read expected_file
16
+ File.open given_file do |fr|
17
+ given = gly_process(fr).string
18
+ assert_equal expected, given
19
+ end
20
+ end
21
+ end
22
+
23
+ here = File.dirname __FILE__
24
+ Dir.glob(File.join(here, 'examples/gly/given/*.gly')).each do |given|
25
+ expected = given.sub('/given', '/expected').sub('.gly', '.gabc')
26
+ example_test_case given, expected
27
+ end
28
+ end
@@ -0,0 +1,9 @@
1
+ # test-suite dynamically created from examples in subfolders
2
+
3
+ def create_test_suite(given_file, expected_file)
4
+
5
+ end
6
+
7
+ Dir.glob('examples/gly/**/*.gly').each do |given|
8
+ expected = given.sub('/given', '/expected').sub('.gly', '.gabc')
9
+ end
@@ -0,0 +1,2 @@
1
+ %%
2
+ a(i) a(i)
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) hej(h)hou(h) hej(h) (,) hou(f) (;) hej(h) (:) hou(f) (::) hej(h)
@@ -0,0 +1,2 @@
1
+ (c4)
2
+ hej(h) (,) hou(f) (;) hej(h) (:) hou(f) (::) hej(h)
@@ -0,0 +1,4 @@
1
+ mode: 8;
2
+ % description: Basic alleluia;
3
+ %%
4
+ (c4) A(g)lle(gh)lu(g)ia(g)
@@ -0,0 +1,4 @@
1
+ mode: 8;
2
+ % description: Basic alleluia;
3
+ %%
4
+ (c4) A(g)lle(gh)lu(g)ia(g)
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) A(h)men.(h) V.(::) Di(gf)xi(gh)
@@ -0,0 +1,3 @@
1
+ name: Nativitas gloriosae;
2
+ office-part: laudes, 1. ant.;
3
+ %%
@@ -0,0 +1,4 @@
1
+ name: Nativitas gloriosae;
2
+ office-part: laudes, 1. ant.;
3
+ % unofficial-header: value;
4
+ %%
@@ -0,0 +1,3 @@
1
+ name: Nativitas gloriosae;
2
+ office-part: laudes, 1. ant.;
3
+ %%
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) Na(g)tí(g')vi(g)tas(gd) glo(f)ri(gh)ó(g)sae(g_') *()
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) Na(g)tí(g')vi(g)tas(gd) glo(f)ri(gh)ó(g)sae(g_') *()
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) a(h)men,(h) *(,) a(h)men.(f)
@@ -0,0 +1,2 @@
1
+ %%
2
+ a(h)men,(h) *(,) a(h)men.(f)
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) (g) (g') (g) (gd) (f) (gh) (g) (g_') () (g a ij)
@@ -0,0 +1 @@
1
+ c4 g g' g gd f (gh) g g_' () (g a ij)
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) (g) (g') (g) (gd) (f) (gh) (g) (g_')
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) Na(g)tí(g')vi(g)tas(gd) glo(f)ri(gh)ó(g)sae(g_') *()
@@ -0,0 +1,2 @@
1
+ c4 g g' g gd f gh g g_' ()
2
+ Na -- tí -- vi -- tas glo -- ri -- ó -- sae *
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) Custó(h)
@@ -0,0 +1,3 @@
1
+ a a
2
+ \lyrics
3
+ i i
@@ -0,0 +1,3 @@
1
+ c4
2
+ h h h , f ; h : f :: h
3
+ hej -- hou hej hou hej hou hej
@@ -0,0 +1,3 @@
1
+ c4
2
+ h , f ; h : f :: h
3
+ hej hou hej hou hej
@@ -0,0 +1,9 @@
1
+ \header
2
+ title: Testing document with document header
3
+ author: the author of gly
4
+
5
+ \score
6
+ mode: 8
7
+ description: Basic alleluia
8
+ c4 g gh g g
9
+ A -- lle -- lu -- ia
File without changes
@@ -0,0 +1,2 @@
1
+ a
2
+ \lyrics a
@@ -0,0 +1,2 @@
1
+ c4 h h :: gf gh
2
+ A -- men. !V. Di -- xi
@@ -0,0 +1,2 @@
1
+ name: Nativitas gloriosae
2
+ office-part: laudes, 1. ant.
@@ -0,0 +1,3 @@
1
+ name: Nativitas gloriosae
2
+ office-part: laudes, 1. ant.
3
+ unofficial-header: value
@@ -0,0 +1,2 @@
1
+ name: Nativitas gloriosae
2
+ office-part: laudes, 1. ant.
@@ -0,0 +1,4 @@
1
+ c4 g g' g gd
2
+ f gh g g_' ()
3
+ Na -- tí -- vi -- tas
4
+ glo -- ri -- ó -- sae *
@@ -0,0 +1,5 @@
1
+ c4 g g' g gd
2
+ Na -- tí -- vi -- tas
3
+
4
+ f gh g g_' ()
5
+ glo -- ri -- ó -- sae *
@@ -0,0 +1,4 @@
1
+ c4 g g' g gd
2
+ f gh g g_' ()
3
+ Na -- tí -- vi -- tas
4
+ glo -- ri -- ó -- sae *
@@ -0,0 +1,2 @@
1
+ c4 h h , h f
2
+ a -- men, * a -- men.
@@ -0,0 +1,2 @@
1
+ h h , h f
2
+ a -- men, * a -- men.
@@ -0,0 +1 @@
1
+ c4 g g' g gd f gh g g_'
@@ -0,0 +1 @@
1
+ c4 g g' g gd f (gh) g g_' () (g a ij)
@@ -0,0 +1 @@
1
+ c4 g g' g gd f gh g g_'
@@ -0,0 +1,2 @@
1
+ c4 g g' g gd f gh g g_' ()
2
+ Na -- tí -- vi -- tas glo -- ri -- ó -- sae *
@@ -0,0 +1,4 @@
1
+ \header
2
+ title: Testing document with document header
3
+ author: the author of gly
4
+
@@ -0,0 +1,10 @@
1
+ \score
2
+ mode: 8
3
+ description: Basic alleluia
4
+ c4 g gh g g
5
+ A -- lle -- lu -- ia
6
+
7
+ \score
8
+ description: Another simple alleluia
9
+ c4 g gj hig h
10
+ A -- lle -- lu -- ia
@@ -0,0 +1,7 @@
1
+ \score
2
+ c4 g gh g g
3
+ A -- lle -- lu -- ia
4
+
5
+ \score
6
+ c4 g gj hig h
7
+ A -- lle -- lu -- ia
@@ -0,0 +1,20 @@
1
+ require_relative 'test_helper'
2
+
3
+ # tests of cases that cannot be easily described by a pair
4
+ # of single gly file and resulting gabc file
5
+ class TestNoCrash < GlyTest
6
+ def self.nocrash_test_case(filename)
7
+ case_name = File.basename(filename).sub(/\.[^\.]*\Z/, '')
8
+ define_method "test_#{case_name}" do
9
+ File.open filename do |fr|
10
+ # simply let it convert to test that it does not crash
11
+ gly_process(fr).string
12
+ end
13
+ end
14
+ end
15
+
16
+ here = File.dirname __FILE__
17
+ Dir.glob(File.join(here, 'examples/gly/no_crash/*.gly')).each do |f|
18
+ nocrash_test_case f
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'test_helper'
2
+
3
+ # tests of cases that cannot be easily described by a pair
4
+ # of single gly file and resulting gabc file
5
+ class TestSpecialCases < MiniTest::Test
6
+ def self.loads_without_crash_test(filename)
7
+ case_name = File.basename(given_file).sub(/\.[^\.]*\Z/, '')
8
+ define_method "test_#{case_name}" do
9
+ File.open given_file do |fr|
10
+ gly_process(fr).string
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'examples'
2
+ require_relative 'no_crash'
3
+ require_relative 'string_helpers_test'
@@ -0,0 +1,3 @@
1
+ require 'minitest/autorun'
2
+
3
+ require_relative 'examples'
@@ -0,0 +1,19 @@
1
+ require_relative 'test_helper'
2
+
3
+ class StringHelpersTest < GlyTest
4
+ SH = Gly::StringHelpers
5
+
6
+ examples = [
7
+ ['single_chunk', 'a', ['a']],
8
+ ['simple_whitespace', 'aa aa', ['aa', 'aa']],
9
+ ['leading_trailing_whitespace', ' a a ', ['a', 'a']],
10
+ ['bracketted', '(a)', ['a']],
11
+ ]
12
+
13
+ examples.each do |e|
14
+ name, given, expected = e
15
+ define_method "test_#{name}" do
16
+ assert_equal expected, SH.music_split(given)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ require_relative 'test_helper'
2
+
3
+ class StringHelpersTest < GlyTest
4
+ SH = Gly::StringHelpers
5
+
6
+ def test_simple_whitespace_split
7
+ assert_equal ['aa', 'aa'], SH.music_split('aa aa')
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ $: << File.expand_path('../lib', File.dirname(__FILE__))
2
+ require 'gly'
3
+
4
+ require 'minitest/autorun'
5
+
6
+ require 'minitest/reporters'
7
+ Minitest::Reporters.use!
8
+
9
+ # parent of all gly test classes
10
+ class GlyTest < MiniTest::Test
11
+ # shortcut performing gly->gabc conversion and returning
12
+ # it's results
13
+ def gly_process(gly_io)
14
+ doc = Gly::Parser.new.parse(gly_io)
15
+ Gly::GabcConvertor.new.convert(doc.scores[0])
16
+ end
17
+ end
metadata ADDED
@@ -0,0 +1,193 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gly
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jakub Pavlík
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest-reporters
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1'
41
+ description: "# gly\n\nWriter-friendly Gregorian notation format compiling to gabc.\n\n*GLY*
42
+ is an acronym of \"Gregorio for liLYponders\" or\n\"Gregorio with separate LYrics.\n\n##
43
+ Why\n\nOne of the most popular solutions for typesetting quadratic\nnotation used
44
+ for the Gregorian chant is [Gregorio][gregorio].\n\nHowever, I have several objections
45
+ against it's input format.\nThat led me to designing an alternative, Gregorio-inspired\nnotation
46
+ format, which compiles to pure Gregorio GABC\n(which I don't like writing manually).\n\n##
47
+ Features\n\n* music separated from lyrics\n * no need of the ubiquitous and tedious
48
+ parentheses\n * separation of \"material and form\" -> easy copying of the music
49
+ or\n lyrics alone is possible (useful for a composer)\n * syllabified lyrics
50
+ entered in a format inspired by LilyPond\n* music and lyrics can be interspersed
51
+ as needed\n* no semicolons in the header\n* custom header fields supported (commented
52
+ out in the GABC output;\n as gregorio crashes on headers it doesn't know)\n* several
53
+ scores per file (when compiled to gabc, each becomes\n a separate file)\n* compile
54
+ pdf preview by a single command, without writing any (La)TeX\n\n## Examples\n\nTypical
55
+ GABC source of an antiphon looks like this:\n\n name: Nativitas gloriosae;\n
56
+ \ office-part: laudes, 1. ant.;\n occasion: In Nativitate B. Mariae Virginis;\n
57
+ \ book: Antiphonale Romanum 1912, pg. 704;\n mode: 8;\n initial-style: 1;\n
58
+ \ %%\n \n (c4) NA(g)TI(g)VI(g)TAS(gd) glo(f)ri(gh)ó(g)sae(g) * (,)\n Vír(g)gi(g)nis(hi)
59
+ Ma(gh)rí(gf)ae,(f) (;)\n ex(f) sé(g)mi(h)ne(h) A(hiwji)bra(hg)hae,(g) (;)\n or(gh~)tae(g)
60
+ de(g) tri(g)bu(fe/fgf) Ju(d)da,(d) (;)\n cla(df!gh)ra(g) ex(f) stir(hg~)pe(hi)
61
+ Da(h)vid.(g) (::)\n\nCorresponding GLY may look like this:\n\n name: Nativitas
62
+ gloriosae\n office-part: laudes, 1. ant.\n occasion: In Nativitate B. Mariae
63
+ Virginis\n book: Antiphonale Romanum 1912, pg. 704\n mode: 8\n initial-style:
64
+ 1\n \n c4 g g g gd f gh g g ,\n g g hi gh gf f ;\n f g h h hiwji hg
65
+ g ;\n gh~ g g g fe/fgf d d ;\n df!gh g f hg~ hi h g ::\n \n NA -- TI
66
+ -- VI -- TAS glo -- ri -- ósae *\n Vír -- gi -- nis Ma -- rí -- ae,\n ex sé
67
+ -- mi -- ne A -- bra -- hae,\n or -- tae de tri -- bu Ju -- da,\n cla -- ra
68
+ ex stir -- pe Da -- vid.\n\nOr, with music and lyrics interlaced:\n\n name: Nativitas
69
+ gloriosae\n office-part: laudes, 1. ant.\n occasion: In Nativitate B. Mariae
70
+ Virginis\n book: Antiphonale Romanum 1912, pg. 704\n mode: 8\n initial-style:
71
+ 1\n \n c4 g g g gd f gh g g ,\n NA -- TI -- VI -- TAS glo -- ri -- ósae
72
+ *\n \n g g hi gh gf f ;\n Vír -- gi -- nis Ma -- rí -- ae,\n \n f
73
+ g h h hiwji hg g ;\n ex sé -- mi -- ne A -- bra -- hae,\n \n gh~ g g g
74
+ fe/fgf d d ;\n or -- tae de tri -- bu Ju -- da,\n \n df!gh g f hg~ hi h
75
+ g ::\n cla -- ra ex stir -- pe Da -- vid.\n\nOther arrangements are also possible.
76
+ Order of music and lyrics\nis actually ignored during processing.\n\n## Usage\n\nThis
77
+ gem provides executable `gly`. Run `gly help` for full list\nof subcommands. The
78
+ most important ones are:\n\n`gly gabc FILE1 ...`\n\nconverts given gly file(s) to
79
+ one or more gabc files (one per score,\ni.e. one gly may spawn a bunch of gabcs).\n\n`gly
80
+ preview FILE1 ...`\n\nAttempts to create a pdf document with all scores contained
81
+ in each\ngly file. Expects gregorio and lualatex to be in PATH\nand gregoriotex
82
+ to be installed and accessible by lualatex.\n\n## Tools\n\n[Emacs mode with syntax
83
+ highlighting for gly][elisp]\n\n![Editing gly in emacs](/doc/img/gly_emacs_scr.png)\n\n##
84
+ Run tests\n\nby executing `tests/run.rb`\n\n## License\n\nMIT\n\n[gregorio]: https://github.com/gregorio-project/gregorio\n[elisp]:
85
+ /tree/master/elisp\n"
86
+ email: jkb.pavlik@gmail.com
87
+ executables:
88
+ - gly
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - bin/gly
93
+ - bin/glyfy
94
+ - lib/gly.rb
95
+ - lib/gly.rb~
96
+ - lib/gly/cli.rb
97
+ - lib/gly/cli.rb~
98
+ - lib/gly/document.rb
99
+ - lib/gly/document.rb~
100
+ - lib/gly/gabc_convertor.rb
101
+ - lib/gly/gabc_convertor.rb~
102
+ - lib/gly/headers.rb
103
+ - lib/gly/headers.rb~
104
+ - lib/gly/lyrics.rb
105
+ - lib/gly/lyrics.rb~
106
+ - lib/gly/parsed_score.rb
107
+ - lib/gly/parsed_score.rb~
108
+ - lib/gly/parser.rb
109
+ - lib/gly/parser.rb~
110
+ - lib/gly/string_helpers.rb
111
+ - lib/gly/string_helpers.rb~
112
+ - tests/examples.rb
113
+ - tests/examples.rb~
114
+ - tests/examples/gly/expected/block_lyrics.gabc
115
+ - tests/examples/gly/expected/differentiae.gabc
116
+ - tests/examples/gly/expected/differentiae.gabc~
117
+ - tests/examples/gly/expected/document_header.gabc
118
+ - tests/examples/gly/expected/document_header.gabc~
119
+ - tests/examples/gly/expected/empty.gabc
120
+ - tests/examples/gly/expected/explicit_lyrics.gabc
121
+ - tests/examples/gly/expected/explicit_lyrics.gabc~
122
+ - tests/examples/gly/expected/explicit_score.gabc
123
+ - tests/examples/gly/expected/force_divisio_lyrics.gabc
124
+ - tests/examples/gly/expected/header.gabc
125
+ - tests/examples/gly/expected/header_unofficial.gabc
126
+ - tests/examples/gly/expected/header_unofficial.gabc~
127
+ - tests/examples/gly/expected/multiline.gabc
128
+ - tests/examples/gly/expected/multiline_interlaced.gabc
129
+ - tests/examples/gly/expected/nonlyrical_lyrics.gabc
130
+ - tests/examples/gly/expected/nonlyrical_lyrics.gabc~
131
+ - tests/examples/gly/expected/notes_bracketted.gabc
132
+ - tests/examples/gly/expected/notes_bracketted.gabc~
133
+ - tests/examples/gly/expected/notes_only.gabc
134
+ - tests/examples/gly/expected/notes_with_lyrics.gabc
135
+ - tests/examples/gly/expected/notes_with_lyrics.gabc~
136
+ - tests/examples/gly/expected/unsyllabified_lyrics.gabc
137
+ - tests/examples/gly/given/block_lyrics.gly
138
+ - tests/examples/gly/given/differentiae.gly
139
+ - tests/examples/gly/given/differentiae.gly~
140
+ - tests/examples/gly/given/document_header.gly
141
+ - tests/examples/gly/given/empty.gly
142
+ - tests/examples/gly/given/explicit_lyrics.gly
143
+ - tests/examples/gly/given/explicit_score.gly
144
+ - tests/examples/gly/given/force_divisio_lyrics.gly
145
+ - tests/examples/gly/given/header.gly
146
+ - tests/examples/gly/given/header_unofficial.gly
147
+ - tests/examples/gly/given/header_unofficial.gly~
148
+ - tests/examples/gly/given/multiline.gly
149
+ - tests/examples/gly/given/multiline_interlaced.gly
150
+ - tests/examples/gly/given/multiline_interlaced.gly~
151
+ - tests/examples/gly/given/nonlyrical_lyrics.gly
152
+ - tests/examples/gly/given/nonlyrical_lyrics.gly~
153
+ - tests/examples/gly/given/notes_bracketted.gabc~
154
+ - tests/examples/gly/given/notes_bracketted.gly
155
+ - tests/examples/gly/given/notes_only.gly
156
+ - tests/examples/gly/given/notes_with_lyrics.gly
157
+ - tests/examples/gly/given/unsyllabified_lyrics.gly
158
+ - tests/examples/gly/no_crash/document_header.gly~
159
+ - tests/examples/gly/no_crash/multiple_scores.gly
160
+ - tests/examples/gly/no_crash/multiple_scores.gly~
161
+ - tests/no_crash.rb
162
+ - tests/programmed_examples.rb~
163
+ - tests/run.rb
164
+ - tests/run.rb~
165
+ - tests/string_helpers_test.rb
166
+ - tests/string_helpers_test.rb~
167
+ - tests/test_helper.rb
168
+ homepage: http://github.com/igneus/gly
169
+ licenses:
170
+ - MIT
171
+ metadata: {}
172
+ post_install_message:
173
+ rdoc_options: []
174
+ require_paths:
175
+ - lib
176
+ required_ruby_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ required_rubygems_version: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ requirements: []
187
+ rubyforge_project:
188
+ rubygems_version: 2.2.2
189
+ signing_key:
190
+ specification_version: 4
191
+ summary: Writer-friendly Gregorian notation format compiling to gabc
192
+ test_files: []
193
+ has_rdoc: