# Treetop (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