lydown 0.7.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +25 -14
  3. data/bin/lydown +1 -122
  4. data/lib/lydown.rb +3 -3
  5. data/lib/lydown/cli.rb +4 -0
  6. data/lib/lydown/cli/commands.rb +98 -0
  7. data/lib/lydown/cli/compiler.rb +11 -14
  8. data/lib/lydown/cli/diff.rb +3 -3
  9. data/lib/lydown/cli/output.rb +18 -0
  10. data/lib/lydown/cli/proofing.rb +8 -6
  11. data/lib/lydown/cli/support.rb +34 -0
  12. data/lib/lydown/cli/translation.rb +73 -0
  13. data/lib/lydown/core_ext.rb +1 -1
  14. data/lib/lydown/lilypond.rb +49 -11
  15. data/lib/lydown/parsing.rb +37 -6
  16. data/lib/lydown/parsing/nodes.rb +57 -56
  17. data/lib/lydown/rendering.rb +0 -9
  18. data/lib/lydown/rendering/base.rb +2 -2
  19. data/lib/lydown/rendering/command.rb +2 -2
  20. data/lib/lydown/rendering/comments.rb +1 -1
  21. data/lib/lydown/rendering/figures.rb +14 -14
  22. data/lib/lydown/rendering/lib.ly +22 -0
  23. data/lib/lydown/rendering/lyrics.rb +2 -2
  24. data/lib/lydown/rendering/movement.rb +2 -2
  25. data/lib/lydown/rendering/music.rb +46 -46
  26. data/lib/lydown/rendering/notes.rb +149 -70
  27. data/lib/lydown/rendering/settings.rb +21 -16
  28. data/lib/lydown/rendering/skipping.rb +1 -1
  29. data/lib/lydown/rendering/source_ref.rb +4 -4
  30. data/lib/lydown/rendering/staff.rb +8 -4
  31. data/lib/lydown/rendering/voices.rb +9 -9
  32. data/lib/lydown/templates.rb +9 -0
  33. data/lib/lydown/templates/lilypond_doc.erb +1 -1
  34. data/lib/lydown/templates/movement.erb +9 -1
  35. data/lib/lydown/templates/part.erb +12 -3
  36. data/lib/lydown/translation.rb +17 -0
  37. data/lib/lydown/translation/ripple.rb +30 -0
  38. data/lib/lydown/translation/ripple/nodes.rb +191 -0
  39. data/lib/lydown/translation/ripple/ripple.treetop +100 -0
  40. data/lib/lydown/version.rb +1 -1
  41. data/lib/lydown/work.rb +144 -198
  42. data/lib/lydown/work_context.rb +152 -0
  43. 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
@@ -62,7 +62,7 @@ class Hash
62
62
  if @deep && k.is_a?(String) && k =~ /\//
63
63
  lookup(k)
64
64
  elsif @deep && k.is_a?(Symbol)
65
- old_get(k.to_s)
65
+ old_get(k) || old_get(k.to_s)
66
66
  else
67
67
  old_get(k)
68
68
  end
@@ -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
- p e.backtrace
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 -lERROR '
57
+ cmd = 'lilypond '
51
58
  cmd << "-o #{opts[:output_filename]} "
52
59
  cmd << "-dno-point-and-click "
53
- cmd << "--#{opts[:format]} " if opts[:format]
54
- cmd << '-s - 2>&1'
60
+ cmd << "--#{opts[:format]} " if format
61
+ cmd << ' - '
55
62
 
56
63
  err_info = ''
57
- IO.popen(cmd, 'r+') do |f|
58
- f.puts source
59
- f.close_write
60
- err_info = f.read
61
- f.close
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 $?.success?
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
@@ -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
- parser = self.new
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
- STDERR.puts error_msg
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
- stream << {type: :source_ref}.merge(opts) if opts[:filename]
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[:source_filename] ? "#{opts[:source_filename]}: " : ""
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
@@ -1,5 +1,5 @@
1
1
  module Lydown::Parsing
2
- module Root
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
- module CommentContent
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
- module Setting
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
- module DurationValue
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
- module TupletValue
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
- module GraceDuration
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
- module Note
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
- module Chord
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
- module StandAloneFigures
188
- include Root
189
-
193
+ class StandAloneFigures < Root
190
194
  def to_stream(stream, opts)
191
- note = {type: :stand_alone_figures}
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
- module BeamOpen
204
+ class BeamOpen < Root
199
205
  def to_stream(stream, opts)
200
- stream << {type: :beam_open}
206
+ add_event(stream, opts, :beam_open)
201
207
  end
202
208
  end
203
209
 
204
- module BeamClose
210
+ class BeamClose < Root
205
211
  def to_stream(stream, opts)
206
- stream << {type: :beam_close}
212
+ add_event(stream, opts, :beam_close)
207
213
  end
208
214
  end
209
215
 
210
- module SlurOpen
216
+ class SlurOpen < Root
211
217
  def to_stream(stream, opts)
212
- stream << {type: :slur_open}
218
+ add_event(stream, opts, :slur_open)
213
219
  end
214
220
  end
215
221
 
216
- module SlurClose
222
+ class SlurClose < Root
217
223
  def to_stream(stream, opts)
218
- stream << {type: :slur_close}
224
+ add_event(stream, opts, :slur_close)
219
225
  end
220
226
  end
221
227
  end
222
228
 
223
- module Tie
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
- module ShortTie
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
- module Rest
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
- module Silence
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 Root
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
- module Lyrics
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
- module StreamIndex
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 << {type: :barline, barline: text_value}
320
+ stream << event_hash(stream, opts, {
321
+ type: :barline,
322
+ barline: text_value
323
+ })
320
324
  end
321
325
  end
322
326
 
323
- module Command
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
- module VoiceSelector
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
- module SourceRef
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)