# Treetop (http://treetop.rubyforge.org/) grammar for package definitions from # the early days. # Some aspects of this grammar are significantly dumber than they could be # because: # # * We want to treat statements as identically as possible to their # command-line equivalents. # * Treetop parse errors are pretty inscrutable at times and we can make # error messages clearer by validating a lot of the terminals ourselves. # # Just say "NO!" to PEG parsers. require 'treetop' require 'fig/grammar/base' require 'fig/grammar/version' module Fig module Grammar grammar V0 include Fig::Grammar::Base include Fig::Grammar::Version rule package optional_ws grammar_version:grammar_version? ws* statements:(package_statement*) optional_ws { def to_package(unparsed_package, build_state) return build_state.new_package_statement( unparsed_package, grammar_version, statements ) end } end rule package_statement archive / resource / retrieve / config end rule archive statement_start:'archive' ws+ location:asset_location { def to_package_statement(build_state) return build_state.new_asset_statement( Statement::Archive, statement_start, location.location ) end } end rule resource statement_start:'resource' ws+ location:asset_location { def to_package_statement(build_state) return build_state.new_asset_statement( Statement::Resource, statement_start, location.location ) end } end rule retrieve statement_start:'retrieve' ws+ variable:environment_variable_name '->' path:retrieve_path ws+ { def to_package_statement(build_state) return build_state.new_retrieve_statement( statement_start, variable, path ) end } end rule config statement_start:'config' ws+ config_name ws+ statements:config_statement* 'end' ws+ { def to_package_statement(build_state) return build_state.new_configuration_statement( statement_start, config_name, statements ) end } end rule config_statement override / include / command / path / set end rule include statement_start:'include' ws+ descriptor_string ws+ { def to_config_statement(build_state) return build_state.new_include_statement( statement_start, descriptor_string ) end } end rule override statement_start:'override' ws+ descriptor_string ws+ { def to_config_statement(build_state) return build_state.new_override_statement( statement_start, descriptor_string ) end } end rule set statement_start:'set' ws+ environment_variable_name_value ws+ { def to_config_statement(build_state) return build_state.new_environment_variable_statement( Statement::Set, statement_start, environment_variable_name_value ) end } end rule path statement_start:('add' / 'append' / 'path') ws+ environment_variable_name_value ws+ { def to_config_statement(build_state) return build_state.new_environment_variable_statement( Statement::Path, statement_start, environment_variable_name_value ) end } end rule command statement_start:'command' ws+ command_line ws+ { def to_config_statement(build_state) return build_state.new_v0_command_statement( statement_start, command_line ) end } end rule command_line '"' [^"]* '"' end # Terminals rule descriptor_string [\S]+ end rule config_name [a-zA-Z0-9_.-]+ end rule environment_variable_name [a-zA-Z0-9_]+ end rule environment_variable_name_value [\S]+ end rule asset_location # Unquoted allows globbing for files, quoted does not. # # Unquoted, anything but: # @ - To allow for package substitution # ' - Future expansion # "<>| - Characters not allowed in filenames on Windows # \s - Necessary for the "ws" token to work (location:[^@'"<>|\s]+ ws) # Quoted, anything but: # @ - To allow for package substitution # ' - Future expansion # "<>| - Characters not allowed in filenames on Windows # *?\[\]{} - Characters significant to Dir.glob() # \s - We just don't want these. :] / ('"' location:[^@'"<>|*?\[\]{}\s]+ '"' ws) end rule retrieve_path [a-zA-Z0-9_/.\[\]-]+ end end end end