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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
-