shellopts 2.0.0.pre.1 → 2.0.0.pre.8
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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/TODO +6 -0
- data/lib/main.rb +1 -0
- data/lib/shellopts.rb +140 -44
- data/lib/shellopts/args.rb +48 -0
- data/lib/shellopts/ast/command.rb +6 -6
- data/lib/shellopts/compiler.rb +19 -21
- data/lib/shellopts/generator.rb +3 -3
- data/lib/shellopts/grammar/command.rb +12 -13
- data/lib/shellopts/grammar/node.rb +11 -3
- data/lib/shellopts/grammar/option.rb +2 -1
- data/lib/shellopts/grammar/program.rb +4 -4
- data/lib/shellopts/idr.rb +71 -44
- data/lib/shellopts/main.rb +10 -0
- data/lib/shellopts/option_struct.rb +62 -159
- data/lib/shellopts/parser.rb +11 -11
- data/lib/shellopts/shellopts.rb +53 -35
- data/lib/shellopts/version.rb +1 -1
- metadata +5 -5
- data/lib/shellopts/messenger.rb +0 -71
- data/lib/shellopts/utils.rb +0 -16
- data/rs +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b967bc79076cae64e2c68c3ef559568a4324d94f7d87800937b746e09755fcfe
|
4
|
+
data.tar.gz: 14c6a5866c6b33da7f7a27f050de46d03b7936842c366fb7b32121324f17d7b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: de339ab12a1ef41f75cbb386a6778adc0dca989f64e9180780c1e00276303824dacca681ccba287f746e0bf7b436b717384be48feb02b4b9faf4be3852c533a5
|
7
|
+
data.tar.gz: a4b286f8297d56afc672a8e9f21119580496c820a8377a419a1aca1905a08e67a5763ed1aa14678137a9c739f120d673a1227d7a18233b43a482e8c43c603d92
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-2.
|
1
|
+
ruby-2.6.6
|
data/TODO
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
|
2
2
|
TODO
|
3
|
+
o Rethink #error and #fail <- The use-case is one-file ruby scripts. Idea: Only use in main exe file?
|
4
|
+
o Create exceptions: UserError SystemFail and allow them to be used instead of #error and #fail
|
5
|
+
? Remove ! from OptionStruct#subcommand return value. We know we're
|
6
|
+
processing commands so there is no need to have a distinct name and it
|
7
|
+
feels a lot more intuitive without it
|
3
8
|
o Add validation block to ShellOpts class methods
|
4
9
|
o Get rid of key_name. Define #name on Grammar::Node instead
|
5
10
|
o Define #name to the string name of the option/command without prefixed '--'
|
@@ -40,6 +45,7 @@ TODO
|
|
40
45
|
o Long version usage strings (major release)
|
41
46
|
o Doc: Example of processing of sub-commands and sub-sub-commands
|
42
47
|
|
48
|
+
+ Add a 'mandatory' argument to #subcommand
|
43
49
|
+ More tests
|
44
50
|
+ More doc
|
45
51
|
+ Implement value-name-before-flags rule
|
data/lib/main.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
hej
|
data/lib/shellopts.rb
CHANGED
@@ -4,11 +4,10 @@ require 'shellopts/compiler.rb'
|
|
4
4
|
require 'shellopts/parser.rb'
|
5
5
|
require 'shellopts/generator.rb'
|
6
6
|
require 'shellopts/option_struct.rb'
|
7
|
-
require 'shellopts/
|
8
|
-
require 'shellopts/utils.rb'
|
7
|
+
require 'shellopts/main.rb'
|
9
8
|
|
10
9
|
# Name of program. Defined as the basename of the program file
|
11
|
-
PROGRAM = File.basename($PROGRAM_NAME)
|
10
|
+
#PROGRAM = File.basename($PROGRAM_NAME)
|
12
11
|
|
13
12
|
# ShellOpts main Module
|
14
13
|
#
|
@@ -21,7 +20,7 @@ PROGRAM = File.basename($PROGRAM_NAME)
|
|
21
20
|
#
|
22
21
|
# For example; the following process and convert a command line into a struct
|
23
22
|
# representation and also sets ShellOpts.shellopts object so that the #error
|
24
|
-
# method can print a relevant
|
23
|
+
# method can print a relevant spec string:
|
25
24
|
#
|
26
25
|
# USAGE = "a,all f,file=FILE -- ARG1 ARG2"
|
27
26
|
# opts, args = ShellOpts.as_struct(USAGE, ARGV)
|
@@ -40,6 +39,9 @@ PROGRAM = File.basename($PROGRAM_NAME)
|
|
40
39
|
# hash, args = ShellOpts.as_hash(USAGE, ARGV)
|
41
40
|
# struct, args = ShellOpts.as_struct(USAGE, ARGV)
|
42
41
|
#
|
42
|
+
# +args+ is a ShellOpts::Argv object containing the the remaning command line
|
43
|
+
# arguments. Argv is derived from Array
|
44
|
+
#
|
43
45
|
# ShellOpts can raise the exception CompilerError is there is an error in the
|
44
46
|
# USAGE string. If there is an error in the user supplied command line, #error
|
45
47
|
# is called instead and the program terminates with exit code 1. ShellOpts
|
@@ -51,18 +53,64 @@ PROGRAM = File.basename($PROGRAM_NAME)
|
|
51
53
|
# ShellOpts injects the constant PROGRAM into the global scope. It contains the
|
52
54
|
# name of the program
|
53
55
|
#
|
56
|
+
# INCLUDING SHELLOPTS
|
57
|
+
#
|
58
|
+
# ShellOpts can optionally be included in your shell application main file but
|
59
|
+
# it is not supposed to be included anywhere else
|
60
|
+
#
|
61
|
+
# Some behind the scenes magic happen if you include the ShellOpts module in your
|
62
|
+
# main exe file
|
63
|
+
#
|
54
64
|
module ShellOpts
|
65
|
+
def self.default_name()
|
66
|
+
@default_name || defined?(PROGRAM) ? PROGRAM : File.basename($0)
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.default_name=(name)
|
70
|
+
@default_name = name
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.default_usage()
|
74
|
+
@default_usage || defined?(USAGE) ? USAGE : nil
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.default_usage=(usage)
|
78
|
+
@default_usage = usage
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.default_key_type()
|
82
|
+
@default_key_type || ::ShellOpts::DEFAULT_KEY_TYPE
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.default_key_type=(type)
|
86
|
+
@default_key_type = type
|
87
|
+
end
|
88
|
+
|
55
89
|
# Base class for ShellOpts exceptions
|
56
90
|
class Error < RuntimeError; end
|
57
91
|
|
58
|
-
# Raised when a syntax error is detected in the
|
92
|
+
# Raised when a syntax error is detected in the spec string
|
59
93
|
class CompilerError < Error
|
60
|
-
def initialize(start,
|
61
|
-
super(
|
94
|
+
def initialize(start, usage)
|
95
|
+
super(usage)
|
62
96
|
set_backtrace(caller(start))
|
63
97
|
end
|
64
98
|
end
|
65
99
|
|
100
|
+
# Raised when an error is detected in the command line
|
101
|
+
class ParserError < Error; end
|
102
|
+
|
103
|
+
# Raised when the command line error is caused by the user. It is raised by
|
104
|
+
# the parser but can also be used by the application if the command line
|
105
|
+
# fails a semantic check
|
106
|
+
class UserError < ParserError; end
|
107
|
+
|
108
|
+
# Raised when the error is caused by a failed assumption about the system. It
|
109
|
+
# is not raised by the ShellOpts library as it only concerns itself with
|
110
|
+
# command line syntax but can be used by the application to report a failure
|
111
|
+
# through ShellOpts#fail method when the ShellOpts module is included
|
112
|
+
class SystemFail < Error; end
|
113
|
+
|
66
114
|
# Raised when an error is detected during conversion from the Idr to array,
|
67
115
|
# hash, or struct
|
68
116
|
class ConversionError < Error; end
|
@@ -73,74 +121,122 @@ module ShellOpts
|
|
73
121
|
# The current compilation object. It is set by #process
|
74
122
|
def self.shellopts() @shellopts end
|
75
123
|
|
76
|
-
#
|
77
|
-
def
|
124
|
+
# Name of program
|
125
|
+
def program_name() shellopts!.name end
|
126
|
+
def program_name=(name) shellopts!.name = name end
|
127
|
+
|
128
|
+
# Usage string
|
129
|
+
def usage() shellopts!.spec end
|
130
|
+
def usage=(spec) shellopts!.spec = spec end
|
131
|
+
|
132
|
+
# Process command line, set current shellopts object, and return it.
|
133
|
+
# Remaining arguments from the command line can be accessed through
|
134
|
+
# +shellopts.args+
|
135
|
+
def self.process(spec, argv, name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage)
|
78
136
|
@shellopts.nil? or reset
|
79
|
-
|
80
|
-
@shellopts = ShellOpts.new(usage, argv, name: name, messenger: messenger)
|
137
|
+
@shellopts = ShellOpts.new(spec, argv, name: name, usage: usage)
|
81
138
|
end
|
82
139
|
|
83
|
-
#
|
84
|
-
#
|
85
|
-
# the
|
86
|
-
def self.as_program(
|
87
|
-
|
140
|
+
# Process command line, set current shellopts object, and return a
|
141
|
+
# [Idr::Program, argv] tuple. Automatically includes the ShellOpts module
|
142
|
+
# if called from the main Ruby object (ie. your executable)
|
143
|
+
def self.as_program(spec, argv, name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage)
|
144
|
+
Main.main.send(:include, ::ShellOpts) if caller.last =~ Main::CALLER_RE
|
145
|
+
process(spec, argv, name: name, usage: usage)
|
88
146
|
[shellopts.idr, shellopts.args]
|
89
147
|
end
|
90
148
|
|
91
|
-
# Process command line, set current shellopts object, and return a [array,
|
92
|
-
# tuple.
|
93
|
-
#
|
94
|
-
def self.as_array(
|
95
|
-
|
149
|
+
# Process command line, set current shellopts object, and return a [array,
|
150
|
+
# argv] tuple. Automatically includes the ShellOpts module if called from the
|
151
|
+
# main Ruby object (ie. your executable)
|
152
|
+
def self.as_array(spec, argv, name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage)
|
153
|
+
Main.main.send(:include, ::ShellOpts) if caller.last =~ Main::CALLER_RE
|
154
|
+
process(spec, argv, name: name, usage: usage)
|
96
155
|
[shellopts.to_a, shellopts.args]
|
97
156
|
end
|
98
157
|
|
99
|
-
# Process command line, set current shellopts object, and return a [hash,
|
100
|
-
# tuple.
|
101
|
-
#
|
102
|
-
def self.as_hash(
|
103
|
-
|
104
|
-
|
158
|
+
# Process command line, set current shellopts object, and return a [hash,
|
159
|
+
# argv] tuple. Automatically includes the ShellOpts module if called from the
|
160
|
+
# main Ruby object (ie. your executable)
|
161
|
+
def self.as_hash(
|
162
|
+
spec, argv,
|
163
|
+
name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage,
|
164
|
+
key_type: ::ShellOpts.default_key_type,
|
165
|
+
aliases: {})
|
166
|
+
Main.main.send(:include, ::ShellOpts) if caller.last =~ Main::CALLER_RE
|
167
|
+
process(spec, argv, name: name, usage: usage)
|
168
|
+
[shellopts.to_h(key_type: key_type, aliases: aliases), shellopts.args]
|
105
169
|
end
|
106
170
|
|
107
|
-
# Process command line, set current shellopts object, and return a [struct,
|
108
|
-
# tuple.
|
109
|
-
#
|
110
|
-
def self.as_struct(
|
111
|
-
|
112
|
-
|
171
|
+
# Process command line, set current shellopts object, and return a [struct,
|
172
|
+
# argv] tuple. Automatically includes the ShellOpts module if called from the
|
173
|
+
# main Ruby object (ie. your executable)
|
174
|
+
def self.as_struct(
|
175
|
+
spec, argv,
|
176
|
+
name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage,
|
177
|
+
aliases: {})
|
178
|
+
Main.main.send(:include, ::ShellOpts) if caller.last =~ Main::CALLER_RE
|
179
|
+
process(spec, argv, name: name, usage: usage)
|
180
|
+
[shellopts.to_struct(aliases: aliases), shellopts.args]
|
113
181
|
end
|
114
182
|
|
115
183
|
# Process command line, set current shellopts object, and then iterate
|
116
184
|
# options and commands as an array. Returns an enumerator to the array
|
117
185
|
# representation of the current shellopts object if not given a block
|
118
|
-
# argument
|
119
|
-
|
120
|
-
|
186
|
+
# argument. Automatically includes the ShellOpts module if called from the
|
187
|
+
# main Ruby object (ie. your executable)
|
188
|
+
def self.each(spec = nil, argv = nil, name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage, &block)
|
189
|
+
Main.main.send(:include, ::ShellOpts) if caller.last =~ Main::CALLER_RE
|
190
|
+
process(spec, argv, name: name, usage: usage)
|
121
191
|
shellopts.each(&block)
|
122
192
|
end
|
123
193
|
|
124
|
-
# Print error
|
194
|
+
# Print error usage and spec string and exit with status 1. This method
|
125
195
|
# should be called in response to user-errors (eg. specifying an illegal
|
126
196
|
# option)
|
127
|
-
def self.error(*msgs)
|
128
|
-
|
129
|
-
shellopts.error(*msgs)
|
197
|
+
def self.error(*msgs, exit: true)
|
198
|
+
shellopts!.error(msgs, exit: exit)
|
130
199
|
end
|
131
200
|
|
132
|
-
# Print error
|
201
|
+
# Print error usage and exit with status 1. This method should not be
|
133
202
|
# called in response to system errors (eg. disk full)
|
134
|
-
def self.fail(*msgs)
|
135
|
-
|
136
|
-
|
203
|
+
def self.fail(*msgs, exit: true)
|
204
|
+
shellopts!.fail(*msgs, exit: exit)
|
205
|
+
end
|
206
|
+
|
207
|
+
def self.included(base)
|
208
|
+
# base.equal?(Object) is only true when included in main (we hope)
|
209
|
+
if !@is_included_in_main && base.equal?(Object)
|
210
|
+
@is_included_in_main = true
|
211
|
+
at_exit do
|
212
|
+
case $!
|
213
|
+
when ShellOpts::UserError
|
214
|
+
::ShellOpts.error($!.message, exit: false)
|
215
|
+
exit!(1)
|
216
|
+
when ShellOpts::SystemFail
|
217
|
+
::ShellOpts.fail($!.message)
|
218
|
+
exit!(1)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
super
|
137
223
|
end
|
138
224
|
|
139
225
|
private
|
226
|
+
# Default default key type
|
227
|
+
DEFAULT_KEY_TYPE = :name
|
228
|
+
|
140
229
|
# Reset state variables
|
141
230
|
def self.reset()
|
142
231
|
@shellopts = nil
|
143
232
|
end
|
144
233
|
|
234
|
+
# (shorthand) Raise an InternalError if shellopts is nil. Return shellopts
|
235
|
+
def self.shellopts!
|
236
|
+
::ShellOpts.shellopts or raise UserError, "No ShellOpts.shellopts object"
|
237
|
+
end
|
238
|
+
|
145
239
|
@shellopts = nil
|
240
|
+
@is_included_in_main = false
|
146
241
|
end
|
242
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
|
2
|
+
module ShellOpts
|
3
|
+
# Specialization of Array for arguments lists. Args extends Array with a
|
4
|
+
# #extract and an #expect method to extract elements from the array. The
|
5
|
+
# methods call #error() in response to errors
|
6
|
+
class Args < Array
|
7
|
+
def initialize(shellopts, *args)
|
8
|
+
@shellopts = shellopts
|
9
|
+
super(*args)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Remove and return elements from beginning of the array. If
|
13
|
+
# +count_or_range+ is a number, that number of elements will be returned.
|
14
|
+
# If the count is one, a simple value is returned instead of an array. If
|
15
|
+
# the count is negative, the elements will be removed from the end of the
|
16
|
+
# array. If +count_or_range+ is a range, the number of elements returned
|
17
|
+
# will be in that range. The range can't contain negative numbers #expect
|
18
|
+
# calls #error() if there's is not enough elements in the array to satisfy
|
19
|
+
# the request
|
20
|
+
def extract(count_or_range, message = nil)
|
21
|
+
if count_or_range.is_a?(Range)
|
22
|
+
range = count_or_range
|
23
|
+
range.min <= self.size or inoa(message)
|
24
|
+
n_extract = [self.size, range.max].min
|
25
|
+
n_extend = range.max > self.size ? range.max - self.size : 0
|
26
|
+
r = self.shift(n_extract) + Array.new(n_extend)
|
27
|
+
else
|
28
|
+
count = count_or_range
|
29
|
+
self.size >= count.abs or inoa(message)
|
30
|
+
start = count >= 0 ? 0 : size + count
|
31
|
+
r = slice!(start, count.abs)
|
32
|
+
r.size == 0 ? nil : (r.size == 1 ? r.first : r)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# As extract except it doesn't allow negative counts and that the array is
|
37
|
+
# expect to be emptied by the operation
|
38
|
+
def expect(count_or_range, message = nil)
|
39
|
+
count_or_range === self.size or inoa(message)
|
40
|
+
extract(count_or_range) # Can't fail
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def inoa(message = nil)
|
45
|
+
raise ShellOpts::UserError, message || "Illegal number of arguments"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -7,17 +7,17 @@ module ShellOpts
|
|
7
7
|
|
8
8
|
# Optional sub-command (Ast::Command). Initially nil but assigned by the
|
9
9
|
# parser
|
10
|
-
attr_accessor :
|
10
|
+
attr_accessor :subcommand
|
11
11
|
|
12
12
|
def initialize(grammar, name)
|
13
13
|
super(grammar, name)
|
14
14
|
@options = []
|
15
|
-
@
|
15
|
+
@subcommand = nil
|
16
16
|
end
|
17
17
|
|
18
18
|
# Array of option or command tuples
|
19
19
|
def values
|
20
|
-
(options + (Array(
|
20
|
+
(options + (Array(subcommand || []))).map { |node| node.to_tuple }
|
21
21
|
end
|
22
22
|
|
23
23
|
# :nocov:
|
@@ -26,10 +26,10 @@ module ShellOpts
|
|
26
26
|
yield if block_given?
|
27
27
|
puts "options:"
|
28
28
|
indent { options.each { |opt| opt.dump } }
|
29
|
-
print "
|
30
|
-
if
|
29
|
+
print "subcommand:"
|
30
|
+
if subcommand
|
31
31
|
puts
|
32
|
-
indent {
|
32
|
+
indent { subcommand.dump }
|
33
33
|
else
|
34
34
|
puts "nil"
|
35
35
|
end
|
data/lib/shellopts/compiler.rb
CHANGED
@@ -29,11 +29,11 @@ module ShellOpts
|
|
29
29
|
def initialize(name, source)
|
30
30
|
@name, @tokens = name, source.split(/\s+/).reject(&:empty?)
|
31
31
|
|
32
|
-
# @
|
32
|
+
# @subcommands_by_path is an hash from subcommand-path to Command or Program
|
33
33
|
# object. The top level Program object has nil as its path.
|
34
|
-
# @
|
35
|
-
# link sub-
|
36
|
-
@
|
34
|
+
# @subcommands_by_path is used to check for uniqueness of subcommands and to
|
35
|
+
# link sub-subcommands to their parents
|
36
|
+
@subcommands_by_path = {}
|
37
37
|
end
|
38
38
|
|
39
39
|
def call
|
@@ -49,30 +49,26 @@ module ShellOpts
|
|
49
49
|
# Returns the current token and advance to the next token
|
50
50
|
def next_token() @tokens.shift end
|
51
51
|
|
52
|
-
def error(msg) # Just a shorthand. Unrelated to ShellOpts.error
|
53
|
-
raise Compiler::Error.new(msg)
|
54
|
-
end
|
55
|
-
|
56
52
|
def compile_program
|
57
|
-
program = @
|
53
|
+
program = @subcommands_by_path[nil] = Grammar::Program.new(@name, compile_options)
|
58
54
|
while curr_token && curr_token != "--"
|
59
|
-
|
55
|
+
compile_subcommand
|
60
56
|
end
|
61
57
|
program.args.concat(@tokens[1..-1]) if curr_token
|
62
58
|
program
|
63
59
|
end
|
64
60
|
|
65
|
-
def
|
61
|
+
def compile_subcommand
|
66
62
|
path = curr_token[0..-2]
|
67
63
|
ident_list = compile_ident_list(path, ".")
|
68
64
|
parent_path = ident_list.size > 1 ? ident_list[0..-2].join(".") : nil
|
69
65
|
name = ident_list[-1]
|
70
66
|
|
71
|
-
parent = @
|
72
|
-
|
73
|
-
!@
|
67
|
+
parent = @subcommands_by_path[parent_path] or
|
68
|
+
raise Compiler::Error, "No such subcommand: #{parent_path.inspect}"
|
69
|
+
!@subcommands_by_path.key?(path) or raise Compiler::Error, "Duplicate subcommand: #{path.inspect}"
|
74
70
|
next_token
|
75
|
-
@
|
71
|
+
@subcommands_by_path[path] = Grammar::Command.new(parent, name, compile_options)
|
76
72
|
end
|
77
73
|
|
78
74
|
def compile_options
|
@@ -81,7 +77,7 @@ module ShellOpts
|
|
81
77
|
option_list << compile_option
|
82
78
|
end
|
83
79
|
dup = option_list.map(&:names).flatten.find_dup and
|
84
|
-
|
80
|
+
raise Compiler::Error, "Duplicate option name: #{dup.inspect}"
|
85
81
|
option_list
|
86
82
|
end
|
87
83
|
|
@@ -102,7 +98,7 @@ module ShellOpts
|
|
102
98
|
long_names = []
|
103
99
|
ident_list = compile_ident_list(names, ",")
|
104
100
|
(dup = ident_list.find_dup).nil? or
|
105
|
-
|
101
|
+
raise Compiler::Error, "Duplicate identifier #{dup.inspect} in #{curr_token.inspect}"
|
106
102
|
ident_list.each { |ident|
|
107
103
|
if ident.size == 1
|
108
104
|
short_names << "-#{ident}"
|
@@ -115,13 +111,15 @@ module ShellOpts
|
|
115
111
|
Grammar::Option.new(short_names, long_names, flags, label)
|
116
112
|
end
|
117
113
|
|
118
|
-
# Compile list of option names or a
|
114
|
+
# Compile list of option names or a subcommand path
|
119
115
|
def compile_ident_list(ident_list_str, sep)
|
120
116
|
ident_list_str.split(sep, -1).map { |str|
|
121
|
-
!str.empty? or
|
122
|
-
|
117
|
+
!str.empty? or
|
118
|
+
raise Compiler::Error, "Empty identifier in #{curr_token.inspect}"
|
119
|
+
!str.start_with?("-") or
|
120
|
+
raise Compiler::Error, "Identifier can't start with '-' in #{curr_token.inspect}"
|
123
121
|
str !~ /([^\w\d#{sep}-])/ or
|
124
|
-
|
122
|
+
raise Compiler::Error, "Illegal character #{$1.inspect} in #{curr_token.inspect}"
|
125
123
|
str
|
126
124
|
}
|
127
125
|
end
|