shellopts 2.0.0.pre.7 → 2.0.0.pre.14

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.
@@ -1,106 +0,0 @@
1
-
2
- require 'shellopts/ast/node.rb'
3
- require 'shellopts/ast/option.rb'
4
- require 'shellopts/ast/command.rb'
5
- require 'shellopts/ast/program.rb'
6
-
7
- module ShellOpts
8
- module Ast
9
- # Parse ARGV according to grammar. Returns a Ast::Program object
10
- def self.parse(grammar, argv)
11
- grammar.is_a?(Grammar::Program) or
12
- raise InternalError, "Expected Grammar::Program object, got #{grammar.class}"
13
- argv.is_a?(Array) or
14
- raise InternalError, "Expected Array object, got #{argv.class}"
15
- Parser.new(grammar, argv).call
16
- end
17
-
18
- private
19
- # Parse a subcommand line
20
- class Parser
21
- class Error < RuntimeError; end
22
-
23
- def initialize(grammar, argv)
24
- @grammar, @argv = grammar, argv.dup
25
- @seen_options = {} # Used to keep track of repeated options
26
- end
27
-
28
- def call
29
- program = Ast::Program.new(@grammar)
30
- parse_subcommand(program)
31
- program.arguments = @argv
32
- program
33
- end
34
-
35
- private
36
- def parse_subcommand(subcommand)
37
- @seen_options = {} # Every new subcommand resets the seen options
38
- while arg = @argv.first
39
- if arg == "--"
40
- @argv.shift
41
- break
42
- elsif arg.start_with?("-")
43
- parse_option(subcommand)
44
- elsif cmd = subcommand.grammar.subcommands[arg]
45
- @argv.shift
46
- subcommand.subcommand = Ast::Command.new(cmd, arg)
47
- parse_subcommand(subcommand.subcommand)
48
- break
49
- else
50
- break
51
- end
52
- end
53
- end
54
-
55
- def parse_option(subcommand)
56
- # Split into name and argument
57
- case @argv.first
58
- when /^(--.+?)(?:=(.*))?$/
59
- name, arg, short = $1, $2, false
60
- when /^(-.)(.+)?$/
61
- name, arg, short = $1, $2, true
62
- end
63
- @argv.shift
64
-
65
- option = subcommand.grammar.options[name] or raise Error, "Unknown option '#{name}'"
66
- !@seen_options.key?(option.key) || option.repeated? or raise Error, "Duplicate option '#{name}'"
67
- @seen_options[option.key] = true
68
-
69
- # Parse (optional) argument
70
- if option.argument?
71
- if arg.nil? && !option.optional?
72
- if !@argv.empty?
73
- arg = @argv.shift
74
- else
75
- raise Error, "Missing argument for option '#{name}'"
76
- end
77
- end
78
- arg &&= parse_arg(option, name, arg)
79
- elsif arg && short
80
- @argv.unshift("-#{arg}")
81
- arg = nil
82
- elsif !arg.nil?
83
- raise Error, "No argument allowed for option '#{name}'"
84
- end
85
-
86
- subcommand.options << Ast::Option.new(option, name, arg)
87
- end
88
-
89
- def parse_arg(option, name, arg)
90
- if option.string?
91
- arg
92
- elsif arg == ""
93
- nil
94
- elsif option.integer?
95
- arg =~ /^-?\d+$/ or raise Error, "Illegal integer in '#{name}' argument: '#{arg}'"
96
- arg.to_i
97
- else # option.float?
98
- # https://stackoverflow.com/a/21891705/2130986
99
- arg =~ /^[+-]?(?:0|[1-9]\d*)(?:\.(?:\d*[1-9]|0))?$/ or
100
- raise Error, "Illegal float in '#{name}' argument: '#{arg}'"
101
- arg.to_f
102
- end
103
- end
104
- end
105
- end
106
- end
@@ -1,116 +0,0 @@
1
-
2
- require "shellopts"
3
-
4
- require "shellopts/args.rb"
5
-
6
- # TODO
7
- #
8
- # PROCESSING
9
- # 1. Compile spec string and yield a grammar
10
- # 2. Parse the options using the grammar and yield an AST
11
- # 3. Construct the Program model from the AST
12
- # 4. Apply defaults to the model
13
- # 6. Run validations on the model
14
- # 5. Create representation from the model
15
- #
16
-
17
- module ShellOpts
18
- # The command line processing object
19
- class ShellOpts
20
- # Name of program
21
- attr_accessor :name
22
-
23
- # Usage string. If #usage is nil, the auto-generated default is used
24
- def usage() @usage || @grammar.usage end
25
- def usage=(usage) @usage = usage end
26
-
27
- # Specification of the command
28
- attr_reader :spec
29
-
30
- # Original argv argument
31
- attr_reader :argv
32
-
33
- # The grammar compiled from the spec string
34
- attr_reader :grammar
35
-
36
- # The AST parsed from the command line arguments
37
- attr_reader :ast
38
-
39
- # The IDR generated from the Ast
40
- attr_reader :idr
41
-
42
- # Compile a spec string into a grammar and use that to parse command line
43
- # arguments
44
- #
45
- # +spec+ is the spec string, and +argv+ the command line (typically the
46
- # global ARGV array). +name+ is the name of the program and defaults to the
47
- # basename of the program
48
- #
49
- # Syntax errors in the spec string are caused by the developer and raise a
50
- # +ShellOpts::CompilerError+ exception. Errors in the +argv+ arguments are
51
- # caused by the user and terminates the program with an error message and a
52
- # short description of its spec
53
- #
54
- # TODO: Change to (name, spec, argv, usage: nil) because
55
- # ShellOpts::ShellOpts isn't a magician like the ShellOpts module
56
- def initialize(spec, argv, name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage)
57
- @name = name
58
- @spec = spec
59
- @usage = usage
60
- @argv = argv
61
- begin
62
- @grammar = Grammar.compile(@name, @spec)
63
- @ast = Ast.parse(@grammar, @argv)
64
- @idr = Idr.generate(self)
65
- rescue Grammar::Compiler::Error => ex
66
- raise CompilerError.new(5, ex.message)
67
- rescue Ast::Parser::Error => ex
68
- raise UserError.new(ex.message)
69
- end
70
- end
71
-
72
- # Return an array representation of options and commands in the same order
73
- # as on the command line. Each option or command is represented by a [name,
74
- # value] pair. The value of an option is be nil if the option didn't have
75
- # an argument and else either a String, Integer, or Float. The value of a
76
- # command is an array of its options and commands
77
- def to_a() idr.to_a end
78
-
79
- # Return a hash representation of the options. See {ShellOpts::OptionsHash}
80
- def to_h(key_type: ::ShellOpts.default_key_type, aliases: {})
81
- @idr.to_h(key_type: :key_type, aliases: aliases)
82
- end
83
-
84
- # TODO
85
- # Return OptionHash object
86
- # def to_hash(...)
87
-
88
- # Return a struct representation of the options. See {ShellOpts::OptionStruct}
89
- def to_struct(key_type: ::ShellOpts.default_key_type, aliases: {})
90
- @idr.to_struct(key_type: key_type, aliases: aliases)
91
- end
92
-
93
- # List of remaining non-option command line arguments. Returns a Argv object
94
- def args() Args.new(self, ast&.arguments) end
95
-
96
- # Iterate options and commands as name/value pairs. Same as +to_a.each+
97
- def each(&block) to_a.each(&block) end
98
-
99
- # Print error messages and spec string and exit with status 1. This method
100
- # should be called in response to user-errors (eg. specifying an illegal
101
- # option)
102
- def error(*msgs, exit: true)
103
- msg = "#{name}: #{msgs.join}\n" + (@usage ? usage : "Usage: #{name} #{usage}")
104
- $stderr.puts msg.rstrip
105
- exit(1) if exit
106
- end
107
-
108
- # Print error message and exit with status 1. This method should called in
109
- # response to system errors (like disk full)
110
- def fail(*msgs, exit: true)
111
- $stderr.puts "#{name}: #{msgs.join}"
112
- exit(1) if exit
113
- end
114
- end
115
- end
116
-