fig 0.1.81 → 0.2.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 (43) hide show
  1. data/Changes +87 -0
  2. data/lib/fig.rb +1 -1
  3. data/lib/fig/command.rb +5 -0
  4. data/lib/fig/command/action/dump_package_definition_for_command_line.rb +62 -0
  5. data/lib/fig/command/action/dump_package_definition_parsed.rb +19 -2
  6. data/lib/fig/command/action/list_local.rb +9 -1
  7. data/lib/fig/command/action/list_remote.rb +9 -1
  8. data/lib/fig/command/action/role/list_variables_in_a_tree.rb +1 -1
  9. data/lib/fig/command/action/run_command_line.rb +1 -1
  10. data/lib/fig/command/action/run_command_statement.rb +4 -2
  11. data/lib/fig/command/options.rb +50 -18
  12. data/lib/fig/command/options/parser.rb +16 -15
  13. data/lib/fig/command/package_applier.rb +5 -3
  14. data/lib/fig/grammar/v0.rb +287 -289
  15. data/lib/fig/grammar/v0.treetop +66 -42
  16. data/lib/fig/grammar/v1.rb +629 -533
  17. data/lib/fig/grammar/v1.treetop +102 -39
  18. data/lib/fig/grammar_monkey_patches.rb +21 -0
  19. data/lib/fig/operating_system.rb +53 -36
  20. data/lib/fig/package_descriptor.rb +1 -12
  21. data/lib/fig/parser.rb +8 -33
  22. data/lib/fig/parser_package_build_state.rb +92 -31
  23. data/lib/fig/repository_package_publisher.rb +2 -2
  24. data/lib/fig/runtime_environment.rb +54 -120
  25. data/lib/fig/statement.rb +6 -6
  26. data/lib/fig/statement/asset.rb +1 -13
  27. data/lib/fig/statement/command.rb +47 -0
  28. data/lib/fig/statement/environment_variable.rb +64 -3
  29. data/lib/fig/statement/grammar_version.rb +4 -0
  30. data/lib/fig/statement/include.rb +4 -0
  31. data/lib/fig/statement/override.rb +4 -0
  32. data/lib/fig/statement/path.rb +40 -16
  33. data/lib/fig/statement/retrieve.rb +61 -5
  34. data/lib/fig/statement/set.rb +16 -19
  35. data/lib/fig/string_tokenizer.rb +63 -25
  36. data/lib/fig/tokenized_string.rb +31 -5
  37. data/lib/fig/tokenized_string/plain_segment.rb +32 -2
  38. data/lib/fig/tokenized_string/token.rb +12 -0
  39. data/lib/fig/unparser.rb +27 -12
  40. data/lib/fig/unparser/v0.rb +4 -5
  41. data/lib/fig/unparser/v1.rb +43 -6
  42. data/lib/fig/url.rb +13 -0
  43. metadata +44 -42
@@ -14,11 +14,10 @@ require 'fig/statement/set'
14
14
 
15
15
  module Fig; end
16
16
 
17
+ # The state of a Package while it is being built by a Parser.
17
18
  class Fig::ParserPackageBuildState
18
- attr_reader :descriptor
19
- attr_reader :source_description
20
-
21
- def initialize(descriptor, source_description)
19
+ def initialize(grammar_version, descriptor, source_description)
20
+ @grammar_version = grammar_version
22
21
  @descriptor = descriptor
23
22
  @source_description = source_description
24
23
  end
@@ -40,7 +39,7 @@ class Fig::ParserPackageBuildState
40
39
  location = node_location(node)
41
40
 
42
41
  return Fig::Statement.position_description(
43
- location[0], location[1], source_description
42
+ location[0], location[1], @source_description
44
43
  )
45
44
  end
46
45
 
@@ -64,8 +63,8 @@ class Fig::ParserPackageBuildState
64
63
  end
65
64
 
66
65
  return Fig::Package.new(
67
- descriptor.name,
68
- descriptor.version,
66
+ @descriptor.name,
67
+ @descriptor.version,
69
68
  directory,
70
69
  statement_objects
71
70
  )
@@ -74,7 +73,7 @@ class Fig::ParserPackageBuildState
74
73
  def new_grammar_version_statement(keyword_node, version_node)
75
74
  return Fig::Statement::GrammarVersion.new(
76
75
  node_location(keyword_node),
77
- source_description,
76
+ @source_description,
78
77
  version_node.text_value.to_i
79
78
  )
80
79
  end
@@ -94,26 +93,29 @@ class Fig::ParserPackageBuildState
94
93
  location = tokenized_location.to_expanded_string
95
94
  need_to_glob = ! tokenized_location.single_quoted?
96
95
  return statement_class.new(
97
- node_location(keyword_node), source_description, location, need_to_glob
96
+ node_location(keyword_node), @source_description, location, need_to_glob
98
97
  )
99
98
  end
100
99
 
101
100
  def new_retrieve_statement(keyword_node, variable_name_node, path_node)
101
+ tokenized_path =
102
+ Fig::Statement::Retrieve.tokenize_path(path_node.text_value) do
103
+ |error_description|
104
+
105
+ raise_invalid_value_parse_error(
106
+ keyword_node, path_node, 'path', error_description
107
+ )
108
+ end
109
+
102
110
  return Fig::Statement::Retrieve.new(
103
111
  node_location(keyword_node),
104
- source_description,
112
+ @source_description,
105
113
  variable_name_node.text_value,
106
- path_node.text_value
114
+ tokenized_path
107
115
  )
108
116
  end
109
117
 
110
118
  def new_configuration_statement(keyword_node, name_node, statements)
111
- if Fig::Parser.strict_keyword? name_node.text_value
112
- raise_invalid_value_parse_error(
113
- keyword_node, name_node, 'name', 'is a keyword.'
114
- )
115
- end
116
-
117
119
  statement_objects = statements.elements.map do
118
120
  |statement|
119
121
 
@@ -122,7 +124,7 @@ class Fig::ParserPackageBuildState
122
124
 
123
125
  return Fig::Statement::Configuration.new(
124
126
  node_location(keyword_node),
125
- source_description,
127
+ @source_description,
126
128
  name_node.text_value,
127
129
  statement_objects
128
130
  )
@@ -138,9 +140,9 @@ class Fig::ParserPackageBuildState
138
140
 
139
141
  return Fig::Statement::Include.new(
140
142
  node_location(keyword_node),
141
- source_description,
143
+ @source_description,
142
144
  include_descriptor,
143
- descriptor
145
+ @descriptor
144
146
  )
145
147
  end
146
148
 
@@ -154,7 +156,7 @@ class Fig::ParserPackageBuildState
154
156
 
155
157
  return Fig::Statement::Override.new(
156
158
  node_location(keyword_node),
157
- source_description,
159
+ @source_description,
158
160
  override_descriptor.name,
159
161
  override_descriptor.version
160
162
  )
@@ -163,25 +165,55 @@ class Fig::ParserPackageBuildState
163
165
  def new_environment_variable_statement(
164
166
  statement_class, keyword_node, value_node
165
167
  )
166
- name, value = statement_class.parse_name_value(value_node.text_value) {
167
- description = statement_class.const_get(:ARGUMENT_DESCRIPTION)
168
- raise Fig::PackageParseError.new(
169
- %Q<Invalid #{keyword_node.text_value} statement: "#{value_node.text_value}" #{description}#{node_location_description(value_node)}>
170
- )
171
- }
168
+ name = value = nil
169
+
170
+ if @grammar_version == 0
171
+ name, value = statement_class.parse_v0_name_value(value_node.text_value) {
172
+ |description|
173
+ raise_invalid_statement_parse_error(
174
+ keyword_node, value_node, description
175
+ )
176
+ }
177
+ else
178
+ name, value = statement_class.parse_name_value(value_node.text_value) {
179
+ |description|
180
+ raise_invalid_statement_parse_error(
181
+ keyword_node, value_node, description
182
+ )
183
+ }
184
+ end
185
+
172
186
  return statement_class.new(
173
- node_location(keyword_node), source_description, name, value
187
+ node_location(keyword_node), @source_description, name, value
174
188
  )
175
189
  end
176
190
 
177
- def new_command_statement(keyword_node, command_node)
191
+ def new_v0_command_statement(keyword_node, command_line_node)
192
+ tokenized_command =
193
+ Fig::Statement::Command.validate_and_process_escapes_in_argument(
194
+ command_line_node.text_value
195
+ ) {
196
+ |description|
197
+ raise_invalid_statement_parse_error(
198
+ keyword_node, command_line_node, description
199
+ )
200
+ }
201
+
202
+ return Fig::Statement::Command.new(
203
+ node_location(keyword_node), @source_description, [tokenized_command]
204
+ )
205
+ end
206
+
207
+ def new_v1_command_statement(keyword_node, command_line)
178
208
  return Fig::Statement::Command.new(
179
209
  node_location(keyword_node),
180
- source_description,
181
- command_node.value.text_value
210
+ @source_description,
211
+ tokenize_v1_command_line(keyword_node, command_line)
182
212
  )
183
213
  end
184
214
 
215
+ private
216
+
185
217
  def raise_invalid_value_parse_error(
186
218
  keyword_node, value_node, value_name, description
187
219
  )
@@ -189,4 +221,33 @@ class Fig::ParserPackageBuildState
189
221
  %Q<Invalid #{value_name} for #{keyword_node.text_value} statement: "#{value_node.text_value}" #{description}#{node_location_description(value_node)}>
190
222
  )
191
223
  end
224
+
225
+ def raise_invalid_statement_parse_error(keyword_node, value_node, description)
226
+ raise Fig::PackageParseError.new(
227
+ %Q<Invalid #{keyword_node.text_value} statement: "#{value_node.text_value}" #{description}#{node_location_description(value_node)}>
228
+ )
229
+ end
230
+
231
+ def tokenize_v1_command_line(keyword_node, command_line)
232
+ tokenized_command_line = []
233
+
234
+ command_line.each do
235
+ |argument_node|
236
+
237
+ unparsed = argument_node.text_value
238
+ next if unparsed.empty?
239
+
240
+ tokenized_command_line <<
241
+ Fig::Statement::Command.validate_and_process_escapes_in_argument(
242
+ unparsed
243
+ ) {
244
+ |description|
245
+ raise_invalid_statement_parse_error(
246
+ keyword_node, argument_node, description
247
+ )
248
+ }
249
+ end
250
+
251
+ return tokenized_command_line
252
+ end
192
253
  end
@@ -113,8 +113,8 @@ class Fig::RepositoryPackagePublisher
113
113
  add_unparsed_text()
114
114
 
115
115
  file_content, explanations = @text_assembler.assemble_package_definition()
116
- if Fig::Logging.debug?
117
- explanations.each {|explanation| Fig::Logging.debug explanation}
116
+ if Fig::Logging.info?
117
+ explanations.each {|explanation| Fig::Logging.info explanation}
118
118
  end
119
119
 
120
120
  begin
@@ -42,7 +42,7 @@ class Fig::RuntimeEnvironment
42
42
  # Indicates that the values from a particular environment variable path
43
43
  # should be copied to a local directory.
44
44
  def add_retrieve(retrieve_statement)
45
- name = retrieve_statement.var
45
+ name = retrieve_statement.variable
46
46
  if @retrieves.has_key?(name)
47
47
  Fig::Logging.warn \
48
48
  %q<About to overwrite "#{name}" retrieve path of "#{@retrieves[name].path}" with "#{retrieve_statement.path}".>
@@ -99,7 +99,7 @@ class Fig::RuntimeEnvironment
99
99
  return
100
100
  end
101
101
 
102
- def execute_command_line(base_package, base_config, descriptor, command_line)
102
+ def expand_command_line(base_package, base_config, descriptor, command_line)
103
103
  package, * =
104
104
  determine_package_for_execution(base_package, base_config, descriptor)
105
105
 
@@ -108,14 +108,12 @@ class Fig::RuntimeEnvironment
108
108
  |argument| expand_command_line_argument(argument, base_package)
109
109
  }
110
110
 
111
- @variables.with_environment do
112
- yield expanded_command_line
113
- end
111
+ @variables.with_environment { yield expanded_command_line }
114
112
 
115
113
  return
116
114
  end
117
115
 
118
- def execute_config(
116
+ def expand_command_statement_from_config(
119
117
  base_package, base_config, descriptor, extra_arguments, &block
120
118
  )
121
119
  package, config_name =
@@ -123,7 +121,7 @@ class Fig::RuntimeEnvironment
123
121
 
124
122
  command_statement = package[config_name].command_statement
125
123
  if command_statement
126
- execute_command(command_statement, extra_arguments, package, &block)
124
+ expand_command(command_statement, extra_arguments, package, &block)
127
125
  else
128
126
  raise Fig::UserInputError.new(
129
127
  %Q<The "#{package.to_s}" package with the "#{config_name}" configuration does not contain a command.>
@@ -213,10 +211,12 @@ class Fig::RuntimeEnvironment
213
211
  @variables[name] = expanded_value
214
212
 
215
213
  if Fig::Logging.debug?
216
- value = statement.value
214
+ tokenized_value = statement.tokenized_value
215
+ escaped_value = tokenized_value.to_escaped_string
217
216
  expanded_message =
218
- expanded_value == value ? '' \
219
- : %Q< (expanded from "#{value}")>
217
+ expanded_value == escaped_value \
218
+ ? '' \
219
+ : %Q< (expanded from "#{escaped_value}")>
220
220
 
221
221
  Fig::Logging.debug(
222
222
  %Q<Set #{name} to "#{expanded_value}"#{expanded_message}.>
@@ -234,10 +234,12 @@ class Fig::RuntimeEnvironment
234
234
  @variables.prepend_variable(name, expanded_value)
235
235
 
236
236
  if Fig::Logging.debug?
237
- value = statement.value
237
+ tokenized_value = statement.tokenized_value
238
+ escaped_value = tokenized_value.to_escaped_string
238
239
  expanded_message =
239
- expanded_value == value ? '' \
240
- : %Q< ("#{value}" expanded to "#{expanded_value}")>
240
+ expanded_value == escaped_value \
241
+ ? '' \
242
+ : %Q< ("#{escaped_value}" expanded to "#{expanded_value}")>
241
243
 
242
244
  Fig::Logging.debug(
243
245
  %Q<Prepending to #{name} resulted in "#{@variables[name]}"#{expanded_message}.>
@@ -323,27 +325,29 @@ class Fig::RuntimeEnvironment
323
325
  return package.primary_config_name || Fig::Package::DEFAULT_CONFIG
324
326
  end
325
327
 
326
- def execute_command(command_statement, extra_arguments, package)
327
- @variables.with_environment do
328
- command =
329
- expand_command_line_argument(
330
- [command_statement.command, extra_arguments].flatten.join(' '),
331
- package
332
- )
328
+ def expand_command(command_statement, extra_arguments, package)
329
+ expanded_command_line =
330
+ [ command_statement.command, extra_arguments ].flatten.map {
331
+ |argument| expand_command_line_argument(argument, package)
332
+ }
333
333
 
334
- yield command
334
+ if command_statement.command.size == 1
335
+ expanded_command_line = [ expanded_command_line.join(' ') ]
335
336
  end
336
337
 
338
+ @variables.with_environment { yield expanded_command_line }
339
+
337
340
  return
338
341
  end
339
342
 
340
343
  def expand_variable_as_path_and_process_retrieves(
341
344
  statement, package, backtrace
342
345
  )
343
- return statement.value unless package && package.name
346
+ tokenized_value = statement.tokenized_value
347
+ return tokenized_value.to_expanded_string { '@' } \
348
+ unless package && package.name
344
349
 
345
- variable_value =
346
- expand_at_signs_in_path(statement.value, package, backtrace)
350
+ variable_value = tokenized_value.to_expanded_string { package.directory }
347
351
 
348
352
  return variable_value if not @retrieves.member?(statement.name)
349
353
 
@@ -399,112 +403,42 @@ class Fig::RuntimeEnvironment
399
403
  return File.join(retrieve_path, File.basename(variable_value))
400
404
  end
401
405
 
402
- def expand_at_signs_in_path(path, package, backtrace)
403
- expanded_path =
404
- replace_at_signs_with_package_references(path, package)
405
- check_for_bad_escape(expanded_path, path, package, backtrace)
406
-
407
- return collapse_backslashes_for_escaped_at_signs(expanded_path)
408
- end
409
-
410
- def replace_at_signs_with_package_references(argument, package)
411
- return argument.gsub(
412
- %r<
413
- (?: ^ | \G) # Zero-width anchor.
414
- ( [^\\@]* ) # A bunch of not-slashes-or-at-signs
415
- ( \\* ) # Any leading backslashes
416
- \@ # The package indicator
417
- >x
418
- ) do |match|
419
- frontmatter, backslashes = $1, $2
420
-
421
- replacement = backslashes.length % 2 == 1 ? '@' : package.directory
422
-
423
- "#{frontmatter}#{backslashes}#{replacement}"
424
- end
425
- end
426
-
427
- def expand_command_line_argument(argument, package)
428
- package_substituted = expand_package_references(argument, package)
429
- check_for_bad_escape(package_substituted, argument, package, nil)
430
-
431
- return collapse_backslashes_for_escaped_at_signs(package_substituted)
432
- end
433
-
434
- def expand_package_references(argument, package)
435
- return argument.gsub(
436
- # TODO: Refactor package name regex into PackageDescriptor constant.
437
- %r<
438
- (?: ^ | \G) # Zero-width anchor.
439
- ( [^\\@]* ) # A bunch of not-slashes-or-at-signs
440
- ( \\* ) # Any leading backslashes
441
- \@ # The package indicator
442
- ( [a-zA-Z0-9_.-]* ) # Package name
443
- >x
444
- ) do |match|
445
- frontmatter, backslashes, package_name = $1, $2, $3
446
-
447
- expand_package_reference(
448
- frontmatter, backslashes, package_name, package
449
- )
450
- end
451
- end
452
-
453
- def expand_package_reference(
454
- frontmatter, backslashes, package_name, starting_package
455
- )
456
- if backslashes.length % 2 == 1
457
- return "#{frontmatter}#{backslashes}@#{package_name}"
458
- end
459
-
460
- package = nil
461
- if ! package_name.empty?
462
- package = get_package(package_name)
463
- if package.nil?
464
- raise_repository_error(
465
- %Q<Command-line referenced the "#{package_name}" package, which has not been referenced by any other package, so there's nothing to substitute with.>,
466
- nil,
467
- nil
468
- )
406
+ def expand_command_line_argument(argument, starting_package)
407
+ return argument.to_expanded_string() do
408
+ |token|
409
+
410
+ package_name = token.raw_value
411
+ package = nil
412
+ if package_name.empty?
413
+ package = starting_package
414
+ else
415
+ package = get_package(package_name)
416
+ if package.nil?
417
+ raise_repository_error(
418
+ %Q<Command-line referenced the "#{package_name}" package, which has not been referenced by any other package, so there's nothing to substitute with.>,
419
+ nil,
420
+ nil
421
+ )
422
+ end
469
423
  end
470
- else
471
- package = starting_package
472
- end
473
-
474
- if package && package.directory
475
- return "#{frontmatter}#{backslashes}#{package.directory}"
476
- end
477
424
 
478
- return "#{frontmatter}#{backslashes}@"
479
- end
425
+ if package && package.directory
426
+ next package.directory
427
+ end
480
428
 
481
- # The value is expected to have had any @ substitution already done, but
482
- # collapsing of escapes not done yet.
483
- def check_for_bad_escape(substituted, original, package, backtrace)
484
- if substituted =~ %r<
485
- (?: ^ | [^\\]) # Start of line or non backslash
486
- (?: \\{2})* # Even number of backslashes (including zero)
487
- ( \\ [^\\@] ) # A bad escape
488
- >x
489
- raise_repository_error(
490
- %Q<Unknown escape "#{$1}" in "#{original}">, backtrace, package
491
- )
429
+ next '@'
492
430
  end
493
-
494
- return
495
- end
496
-
497
- # After @ substitution, we need to get rid of the backslashes in front of
498
- # any escaped @ signs.
499
- def collapse_backslashes_for_escaped_at_signs(string)
500
- return string.gsub(%r< \\ ([\\@]) >x, '\1')
501
431
  end
502
432
 
503
433
  def get_retrieve_path_with_substitution(variable_name, package)
504
434
  retrieve_statement = @retrieves[variable_name]
505
435
  retrieve_statement.referenced(true)
506
436
 
507
- return retrieve_statement.path.gsub(/ \[package\] /x, package.name)
437
+ return retrieve_statement.tokenized_path.to_expanded_string() do
438
+ |token|
439
+
440
+ package.name
441
+ end
508
442
  end
509
443
 
510
444
  def raise_repository_error(message, backtrace, package)