lydown 0.7.2 → 0.9.0
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.
- checksums.yaml +4 -4
- data/README.md +25 -14
- data/bin/lydown +1 -122
- data/lib/lydown.rb +3 -3
- data/lib/lydown/cli.rb +4 -0
- data/lib/lydown/cli/commands.rb +98 -0
- data/lib/lydown/cli/compiler.rb +11 -14
- data/lib/lydown/cli/diff.rb +3 -3
- data/lib/lydown/cli/output.rb +18 -0
- data/lib/lydown/cli/proofing.rb +8 -6
- data/lib/lydown/cli/support.rb +34 -0
- data/lib/lydown/cli/translation.rb +73 -0
- data/lib/lydown/core_ext.rb +1 -1
- data/lib/lydown/lilypond.rb +49 -11
- data/lib/lydown/parsing.rb +37 -6
- data/lib/lydown/parsing/nodes.rb +57 -56
- data/lib/lydown/rendering.rb +0 -9
- data/lib/lydown/rendering/base.rb +2 -2
- data/lib/lydown/rendering/command.rb +2 -2
- data/lib/lydown/rendering/comments.rb +1 -1
- data/lib/lydown/rendering/figures.rb +14 -14
- data/lib/lydown/rendering/lib.ly +22 -0
- data/lib/lydown/rendering/lyrics.rb +2 -2
- data/lib/lydown/rendering/movement.rb +2 -2
- data/lib/lydown/rendering/music.rb +46 -46
- data/lib/lydown/rendering/notes.rb +149 -70
- data/lib/lydown/rendering/settings.rb +21 -16
- data/lib/lydown/rendering/skipping.rb +1 -1
- data/lib/lydown/rendering/source_ref.rb +4 -4
- data/lib/lydown/rendering/staff.rb +8 -4
- data/lib/lydown/rendering/voices.rb +9 -9
- data/lib/lydown/templates.rb +9 -0
- data/lib/lydown/templates/lilypond_doc.erb +1 -1
- data/lib/lydown/templates/movement.erb +9 -1
- data/lib/lydown/templates/part.erb +12 -3
- data/lib/lydown/translation.rb +17 -0
- data/lib/lydown/translation/ripple.rb +30 -0
- data/lib/lydown/translation/ripple/nodes.rb +191 -0
- data/lib/lydown/translation/ripple/ripple.treetop +100 -0
- data/lib/lydown/version.rb +1 -1
- data/lib/lydown/work.rb +144 -198
- data/lib/lydown/work_context.rb +152 -0
- metadata +11 -2
@@ -0,0 +1,34 @@
|
|
1
|
+
module Lydown::CLI::Support
|
2
|
+
def self.copy_options(options)
|
3
|
+
opts = {}.deep!
|
4
|
+
|
5
|
+
options.each {|k, v| opts[k.to_sym] = v}
|
6
|
+
opts
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.detect_filename(options)
|
10
|
+
source = ''
|
11
|
+
if options[:path] == '-'
|
12
|
+
# read source from stdin
|
13
|
+
options[:source] = STDIN.read
|
14
|
+
|
15
|
+
# the output defaults to a file named lydown expect if the format is ly.
|
16
|
+
# In that case the output will be sent to STDOUT.
|
17
|
+
options[:output_filename] ||= 'lydown' unless options[:format] == 'ly'
|
18
|
+
else
|
19
|
+
options[:source_filename] = options[:path]
|
20
|
+
if (options[:path] !~ /\.ld$/) and File.file?(options[:path] + ".ld")
|
21
|
+
options[:path] += ".ld"
|
22
|
+
end
|
23
|
+
|
24
|
+
unless options[:output_filename]
|
25
|
+
if options[:path] == '.'
|
26
|
+
options[:output_filename] = File.basename(FileUtils.pwd)
|
27
|
+
else
|
28
|
+
options[:output_filename] = (options[:path] =~ /^(.+)\.ld$/) ?
|
29
|
+
$1 : options[:path]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Lydown::CLI::Translation
|
4
|
+
class << self
|
5
|
+
def process(opts)
|
6
|
+
if File.directory?(opts[:path])
|
7
|
+
process_directory(opts[:path])
|
8
|
+
elsif File.file?(opts[:path])
|
9
|
+
process_file(opts[:path])
|
10
|
+
elsif File.file?(opts[:path] + '.rpl')
|
11
|
+
process_file(opts[:path] + '.rpl')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def process_directory(path)
|
16
|
+
macros = load_macros(path)
|
17
|
+
Dir["#{path}/*"].entries.each do |entry|
|
18
|
+
if File.file?(entry) && (entry =~ /\.rpl$/)
|
19
|
+
process_file(entry)
|
20
|
+
elsif File.directory?(entry)
|
21
|
+
process_directory(entry)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def process_file(path)
|
27
|
+
ext = File.extname(path)
|
28
|
+
base_path = ext == '.rpl' ? path.gsub(/#{ext}$/, '') : path
|
29
|
+
output_path = base_path + '.ld'
|
30
|
+
lyrics_path = base_path + '.lyr'
|
31
|
+
figures_path = base_path + '.fig'
|
32
|
+
|
33
|
+
$stderr.puts "Translating #{path}..."
|
34
|
+
|
35
|
+
code = {
|
36
|
+
path: path,
|
37
|
+
ripple: IO.read(path),
|
38
|
+
lyrics: File.file?(lyrics_path) && IO.read(lyrics_path),
|
39
|
+
figures: File.file?(figures_path) && IO.read(figures_path),
|
40
|
+
macros: macros_for_path(path)
|
41
|
+
}
|
42
|
+
|
43
|
+
ld_code = Lydown::Translation.process(code)
|
44
|
+
|
45
|
+
File.open(output_path, 'w+') {|f| f.write(ld_code)}
|
46
|
+
end
|
47
|
+
|
48
|
+
WORK_FILENAME = '_work.yml'
|
49
|
+
MOVEMENT_FILENAME = '_movement.yml'
|
50
|
+
|
51
|
+
PATH_MACROS = {}
|
52
|
+
|
53
|
+
def macros_for_path(path)
|
54
|
+
PATH_MACROS[path] ||= load_macros(path)
|
55
|
+
end
|
56
|
+
|
57
|
+
def load_macros(path)
|
58
|
+
base_dir = File.directory?(path) ? path : File.dirname(path)
|
59
|
+
parent_dir = File.expand_path(File.join(base_dir, '..'))
|
60
|
+
|
61
|
+
yml =
|
62
|
+
(YAML.load_file(File.join(parent_dir, WORK_FILENAME)) rescue nil) ||
|
63
|
+
(YAML.load_file(File.join(base_dir, WORK_FILENAME)) rescue nil) ||
|
64
|
+
{}
|
65
|
+
macros = yml['macros'] || {}
|
66
|
+
|
67
|
+
yml =
|
68
|
+
(YAML.load_file(File.join(base_dir, MOVEMENT_FILENAME)) rescue nil) ||
|
69
|
+
{}
|
70
|
+
macros.merge(yml['macros'] || {})
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/lydown/core_ext.rb
CHANGED
data/lib/lydown/lilypond.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'lydown/errors'
|
2
|
+
require 'lydown/cli/output'
|
3
|
+
|
2
4
|
require 'tempfile'
|
3
5
|
require 'fileutils'
|
6
|
+
require 'open3'
|
4
7
|
|
5
8
|
module Lydown
|
6
9
|
module Lilypond
|
@@ -28,8 +31,9 @@ module Lydown
|
|
28
31
|
copy_pages(tmp_target, target, ext)
|
29
32
|
end
|
30
33
|
rescue => e
|
31
|
-
puts e.message
|
32
|
-
|
34
|
+
$stderr.puts e.message
|
35
|
+
$stderr.puts e.backtrace.join("\n")
|
36
|
+
raise e
|
33
37
|
end
|
34
38
|
|
35
39
|
def copy_pages(source, target, ext)
|
@@ -46,25 +50,59 @@ module Lydown
|
|
46
50
|
end
|
47
51
|
|
48
52
|
def invoke(source, opts = {})
|
53
|
+
format = opts[:format]
|
54
|
+
format = nil if format == 'midi'
|
55
|
+
|
49
56
|
# Run lilypond, pipe source into its STDIN, and capture its STDERR
|
50
|
-
cmd = 'lilypond
|
57
|
+
cmd = 'lilypond '
|
51
58
|
cmd << "-o #{opts[:output_filename]} "
|
52
59
|
cmd << "-dno-point-and-click "
|
53
|
-
cmd << "--#{opts[:format]} " if
|
54
|
-
cmd << '
|
60
|
+
cmd << "--#{opts[:format]} " if format
|
61
|
+
cmd << ' - '
|
55
62
|
|
56
63
|
err_info = ''
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
64
|
+
success = false
|
65
|
+
Open3.popen2e(cmd) do |input, output, wait_thr|
|
66
|
+
input.puts source
|
67
|
+
input.close_write
|
68
|
+
err_info = read_lilypond_progress(output, opts)
|
69
|
+
output.close
|
70
|
+
success = wait_thr.value == 0
|
62
71
|
end
|
63
|
-
unless
|
72
|
+
unless success
|
64
73
|
err_info = err_info.lines[0, 3].join
|
65
74
|
raise LydownError, "Lilypond compilation failed:\n#{err_info}"
|
66
75
|
end
|
67
76
|
end
|
77
|
+
|
78
|
+
LILYPOND_STATUS_LINES = %w{
|
79
|
+
Processing
|
80
|
+
Parsing
|
81
|
+
Interpreting
|
82
|
+
Preprocessing
|
83
|
+
Finding
|
84
|
+
Fitting
|
85
|
+
Drawing
|
86
|
+
Layout
|
87
|
+
Converting
|
88
|
+
Success:
|
89
|
+
}
|
90
|
+
STATUS_TOTAL = LILYPOND_STATUS_LINES.size
|
91
|
+
|
92
|
+
def read_lilypond_progress(f, opts)
|
93
|
+
info = ''
|
94
|
+
Lydown::CLI::show_progress('Compile', STATUS_TOTAL) do |bar|
|
95
|
+
while !f.eof?
|
96
|
+
line = f.gets
|
97
|
+
info += line
|
98
|
+
if line =~ /^([^\s]+)/
|
99
|
+
idx = LILYPOND_STATUS_LINES.index($1)
|
100
|
+
bar.progress = idx + 1 if idx
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
info
|
105
|
+
end
|
68
106
|
end
|
69
107
|
end
|
70
108
|
end
|
data/lib/lydown/parsing.rb
CHANGED
@@ -4,26 +4,57 @@ require 'treetop'
|
|
4
4
|
require 'lydown/parsing/nodes'
|
5
5
|
require 'lydown/parsing/lydown.treetop'
|
6
6
|
|
7
|
-
# Treetop.load 'lydown/parsing/lydown'
|
8
|
-
|
9
7
|
class LydownParser
|
10
8
|
def self.parse(source, opts = {})
|
11
|
-
|
9
|
+
if opts[:no_progress]
|
10
|
+
do_parse(source, opts)
|
11
|
+
else
|
12
|
+
Lydown::CLI.show_progress('Parse', source.length * 2) do |bar|
|
13
|
+
stream = do_parse(source, opts)
|
14
|
+
bar.finish
|
15
|
+
stream
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.do_parse(source, opts)
|
21
|
+
parser, ast = self.new
|
12
22
|
ast = parser.parse(source)
|
23
|
+
|
13
24
|
unless ast
|
14
25
|
error_msg = format_parser_error(source, parser, opts)
|
15
|
-
|
26
|
+
$stderr.puts error_msg
|
16
27
|
raise LydownError, error_msg
|
17
28
|
else
|
18
29
|
stream = []
|
30
|
+
ast.to_stream(stream, opts.merge(progress_base: source.length))
|
19
31
|
# insert source ref event into stream if we have a filename ref
|
20
|
-
|
32
|
+
if opts[:filename] && !stream.empty?
|
33
|
+
stream.unshift({type: :source_ref}.merge(opts))
|
34
|
+
end
|
35
|
+
stream
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.parse_macro_group(source, opts)
|
40
|
+
parser, ast = self.new, ast = nil
|
41
|
+
old_bar, $progress_bar = $progress_bar, nil
|
42
|
+
ast = parser.parse(source)
|
43
|
+
unless ast
|
44
|
+
error_msg = format_parser_error(source, parser, opts)
|
45
|
+
$stderr.puts error_msg
|
46
|
+
raise LydownError, error_msg
|
47
|
+
else
|
48
|
+
stream = []
|
21
49
|
ast.to_stream(stream, opts)
|
50
|
+
stream
|
22
51
|
end
|
52
|
+
ensure
|
53
|
+
$progress_bar = old_bar
|
23
54
|
end
|
24
55
|
|
25
56
|
def self.format_parser_error(source, parser, opts)
|
26
|
-
msg = opts[:
|
57
|
+
msg = opts[:filename] ? "#{opts[:filename]}: " : ""
|
27
58
|
if opts[:nice_error]
|
28
59
|
msg << "Unexpected character at line #{parser.failure_line} column #{parser.failure_column}:\n"
|
29
60
|
else
|
data/lib/lydown/parsing/nodes.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Lydown::Parsing
|
2
|
-
module
|
2
|
+
module RootMethods
|
3
3
|
def _to_stream(element, stream, opts)
|
4
4
|
if element.elements
|
5
5
|
element.elements.each do |e|
|
@@ -17,9 +17,9 @@ module Lydown::Parsing
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def event_hash(stream, opts, hash = {})
|
20
|
-
if source = opts[:source]
|
20
|
+
if opts[:proof_mode] && (source = opts[:source])
|
21
21
|
last = stream.last
|
22
|
-
if last && last[:type] == :source_ref && last[:line]
|
22
|
+
if last && (last[:type] == :source_ref) && last[:line]
|
23
23
|
line, column = last[:line], last[:column]
|
24
24
|
else
|
25
25
|
line, column = source.find_line_and_column(interval.first)
|
@@ -34,18 +34,34 @@ module Lydown::Parsing
|
|
34
34
|
else
|
35
35
|
hash
|
36
36
|
end
|
37
|
+
ensure
|
38
|
+
if $progress_bar
|
39
|
+
$progress_bar.progress = (opts[:progress_base] || 0) + interval.end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_event(stream, opts, type)
|
44
|
+
stream << event_hash(stream, opts, {type: type})
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Root < Treetop::Runtime::SyntaxNode
|
49
|
+
include RootMethods
|
50
|
+
def initialize(*args)
|
51
|
+
super
|
52
|
+
if $progress_bar
|
53
|
+
$progress_bar.progress = interval.end
|
54
|
+
end
|
37
55
|
end
|
38
56
|
end
|
39
57
|
|
40
|
-
|
58
|
+
class CommentContent < Root
|
41
59
|
def to_stream(stream, opts)
|
42
60
|
stream << {type: :comment, content: text_value.strip}
|
43
61
|
end
|
44
62
|
end
|
45
63
|
|
46
|
-
|
47
|
-
include Root
|
48
|
-
|
64
|
+
class Setting < Root
|
49
65
|
def to_stream(stream, opts)
|
50
66
|
level = (text_value =~ /^([\s]+)/) ? ($1.length / 2) : 0
|
51
67
|
@setting = event_hash(stream, opts, {
|
@@ -76,9 +92,7 @@ module Lydown::Parsing
|
|
76
92
|
end
|
77
93
|
end
|
78
94
|
|
79
|
-
|
80
|
-
include Root
|
81
|
-
|
95
|
+
class DurationValue < Root
|
82
96
|
def to_stream(stream, opts)
|
83
97
|
stream << event_hash(stream, opts, {
|
84
98
|
type: :duration, value: text_value
|
@@ -86,9 +100,7 @@ module Lydown::Parsing
|
|
86
100
|
end
|
87
101
|
end
|
88
102
|
|
89
|
-
|
90
|
-
include Root
|
91
|
-
|
103
|
+
class TupletValue < Root
|
92
104
|
def to_stream(stream, opts)
|
93
105
|
if text_value =~ /^(\d+)%((\d+)\/(\d+))?$/
|
94
106
|
value, fraction, group_length = $1, $2, $4
|
@@ -107,9 +119,7 @@ module Lydown::Parsing
|
|
107
119
|
end
|
108
120
|
end
|
109
121
|
|
110
|
-
|
111
|
-
include Root
|
112
|
-
|
122
|
+
class GraceDuration < Root
|
113
123
|
GRACE_KIND = {
|
114
124
|
nil => :grace,
|
115
125
|
'/' => :acciaccatura,
|
@@ -125,9 +135,7 @@ module Lydown::Parsing
|
|
125
135
|
end
|
126
136
|
end
|
127
137
|
|
128
|
-
|
129
|
-
include Root
|
130
|
-
|
138
|
+
class Note < Root
|
131
139
|
def to_stream(stream, opts)
|
132
140
|
note = event_hash(stream, opts, {
|
133
141
|
type: :note, raw: text_value
|
@@ -165,9 +173,7 @@ module Lydown::Parsing
|
|
165
173
|
end
|
166
174
|
end
|
167
175
|
|
168
|
-
|
169
|
-
include Root
|
170
|
-
|
176
|
+
class Chord < Root
|
171
177
|
def to_stream(stream, opts)
|
172
178
|
chord = event_hash(stream, opts, {
|
173
179
|
type: :chord, notes: []
|
@@ -184,57 +190,55 @@ module Lydown::Parsing
|
|
184
190
|
end
|
185
191
|
end
|
186
192
|
|
187
|
-
|
188
|
-
include Root
|
189
|
-
|
193
|
+
class StandAloneFigures < Root
|
190
194
|
def to_stream(stream, opts)
|
191
|
-
note = {
|
195
|
+
note = event_hash(stream, opts, {
|
196
|
+
type: :stand_alone_figures
|
197
|
+
})
|
192
198
|
_to_stream(self, note, opts)
|
193
199
|
stream << note
|
194
200
|
end
|
195
201
|
end
|
196
202
|
|
197
203
|
module Phrasing
|
198
|
-
|
204
|
+
class BeamOpen < Root
|
199
205
|
def to_stream(stream, opts)
|
200
|
-
stream
|
206
|
+
add_event(stream, opts, :beam_open)
|
201
207
|
end
|
202
208
|
end
|
203
209
|
|
204
|
-
|
210
|
+
class BeamClose < Root
|
205
211
|
def to_stream(stream, opts)
|
206
|
-
stream
|
212
|
+
add_event(stream, opts, :beam_close)
|
207
213
|
end
|
208
214
|
end
|
209
215
|
|
210
|
-
|
216
|
+
class SlurOpen < Root
|
211
217
|
def to_stream(stream, opts)
|
212
|
-
stream
|
218
|
+
add_event(stream, opts, :slur_open)
|
213
219
|
end
|
214
220
|
end
|
215
221
|
|
216
|
-
|
222
|
+
class SlurClose < Root
|
217
223
|
def to_stream(stream, opts)
|
218
|
-
stream
|
224
|
+
add_event(stream, opts, :slur_close)
|
219
225
|
end
|
220
226
|
end
|
221
227
|
end
|
222
228
|
|
223
|
-
|
229
|
+
class Tie < Root
|
224
230
|
def to_stream(stream, opts)
|
225
|
-
stream << {type: :tie}
|
231
|
+
stream << event_hash(stream, opts, {type: :tie})
|
226
232
|
end
|
227
233
|
end
|
228
234
|
|
229
|
-
|
235
|
+
class ShortTie < Root
|
230
236
|
def to_stream(stream, opts)
|
231
|
-
stream << {type: :short_tie}
|
237
|
+
stream << event_hash(stream, opts, {type: :short_tie})
|
232
238
|
end
|
233
239
|
end
|
234
240
|
|
235
|
-
|
236
|
-
include Root
|
237
|
-
|
241
|
+
class Rest < Root
|
238
242
|
def to_stream(stream, opts)
|
239
243
|
rest = event_hash(stream, opts, {
|
240
244
|
type: :rest, raw: text_value, head: text_value[0]
|
@@ -249,9 +253,7 @@ module Lydown::Parsing
|
|
249
253
|
end
|
250
254
|
end
|
251
255
|
|
252
|
-
|
253
|
-
include Root
|
254
|
-
|
256
|
+
class Silence < Root
|
255
257
|
def to_stream(stream, opts)
|
256
258
|
stream << event_hash(stream, opts, {
|
257
259
|
type: :silence, raw: text_value, head: text_value[0]
|
@@ -260,8 +262,7 @@ module Lydown::Parsing
|
|
260
262
|
end
|
261
263
|
|
262
264
|
module DurationMacroExpression
|
263
|
-
include
|
264
|
-
|
265
|
+
include RootMethods
|
265
266
|
def to_stream(stream, opts)
|
266
267
|
stream << event_hash(stream, opts, {
|
267
268
|
type: :duration_macro, macro: text_value
|
@@ -269,8 +270,7 @@ module Lydown::Parsing
|
|
269
270
|
end
|
270
271
|
end
|
271
272
|
|
272
|
-
|
273
|
-
include Root
|
273
|
+
class Lyrics < Root
|
274
274
|
def to_stream(stream, opts)
|
275
275
|
o = {type: :lyrics}
|
276
276
|
_to_stream(self, o, opts)
|
@@ -304,7 +304,7 @@ module Lydown::Parsing
|
|
304
304
|
end
|
305
305
|
end
|
306
306
|
|
307
|
-
|
307
|
+
class StreamIndex < Root
|
308
308
|
def to_stream(o, opts)
|
309
309
|
idx = (text_value =~ /\(([\d]+)\)/) && $1.to_i
|
310
310
|
if idx.nil?
|
@@ -315,14 +315,16 @@ module Lydown::Parsing
|
|
315
315
|
end
|
316
316
|
|
317
317
|
module Barline
|
318
|
+
include RootMethods
|
318
319
|
def to_stream(stream, opts)
|
319
|
-
stream <<
|
320
|
+
stream << event_hash(stream, opts, {
|
321
|
+
type: :barline,
|
322
|
+
barline: text_value
|
323
|
+
})
|
320
324
|
end
|
321
325
|
end
|
322
326
|
|
323
|
-
|
324
|
-
include Root
|
325
|
-
|
327
|
+
class Command < Root
|
326
328
|
def to_stream(stream, opts)
|
327
329
|
cmd = event_hash(stream, opts, {
|
328
330
|
type: :command, raw: text_value
|
@@ -361,15 +363,14 @@ module Lydown::Parsing
|
|
361
363
|
end
|
362
364
|
end
|
363
365
|
|
364
|
-
|
366
|
+
class VoiceSelector < Root
|
365
367
|
def to_stream(stream, opts)
|
366
368
|
voice = (text_value =~ /^([1234])/) && $1.to_i
|
367
369
|
stream << {type: :voice_select, voice: voice}
|
368
370
|
end
|
369
371
|
end
|
370
372
|
|
371
|
-
|
372
|
-
include Root
|
373
|
+
class SourceRef < Root
|
373
374
|
def to_stream(stream, opts)
|
374
375
|
ref = {type: :source_ref, raw: text_value}
|
375
376
|
_to_stream(self, ref, opts)
|