require "vector.rb"
class Clop_Option
attr_reader :shortname, :longname, :type,
:description, :longdescription, :printname, :defaultvalue
attr_accessor :valuestring
def initialize(def_str)
parse_option_definition(def_str)
end
def parse_option_definition(def_str)
while s = def_str.shift
break if parse_single_lines_done?(s)
end
while s = def_str.shift
break if s =~ /^\s*$/ and def_str[0] =~ /^\s*$/
@longdescription += s + "\n"
end
end
def parse_single_lines_done?(s)
if s !~ /\s*(\w.*?)\s*\:/
raise "\n option definition line has wrong format:\n==> #{s} <==\n"
end
name = $1
content = $'
case name
when /Short\s+(N|n)ame/
@shortname = content.split[0]
when /Long\s+(N|n)ame/
@longname = content.split[0]
when /Value\s+(T|t)ype/
@type = content.sub(/^\s+/,"").sub(/\s*(#.*|$)/,"")
@valuestring = "false" if @type == "bool"
when /Default\s+(V|v)alue/
@defaultvalue = content.sub(/^\s+/,"").sub(/\s*(#.*|$)/,"")
@valuestring = @defaultvalue
when /Global\s+(V|v)ariable/
@globalname = content.split[0]
when /Print\s+(N|n)ame/
@printname = content.sub(/^\s+/,"").sub(/\s*(#.*|$)/,"")
when /Description/
@description = content.sub(/^\s+/,"").sub(/\s*(#.*|$)/,"")
when /Long\s+(D|d)escription/
@longdescription = ""
return true
else
raise "\n option definition line unrecognized:\n==> #{s} <==\n"
end
return false
end
def initialize_global_variable
eval("$#{@globalname} = eval_value") if @globalname
end
def eval_value
case @type
when "bool"
eval(@valuestring)
when "string"
@valuestring
when "int"
@valuestring.to_i
when "float"
@valuestring.to_f
when /^float\s*vector$/
@valuestring.gsub(/[\[,\]]/," ").split.map{|x| x.to_f}.to_v
else
raise "\n type \"#{@type}\" is not recognized"
end
end
def add_tabs(s, reference_size, n)
(1..n).each{|i| s += "\t" if reference_size < 8*i}
return s
end
def to_s
if @type == nil
s = @description + "\n"
elsif @type == "bool"
if eval(@valuestring)
s = @description + "\n"
else
s = ""
end
else
s = @description
s = add_tabs(s, s.size, 4)
s += ": "
if @printname
s += @printname
else
s += @globalname
end
s += " = " unless @printname == ""
s += "\n " if @type =~ /^float\s*vector$/
s += "#{eval("$#{@globalname}")}\n"
end
return s
end
end
class Clop
def initialize(def_str, argv_array = nil)
parse_option_definitions(def_str)
if argv_array
parse_command_line_options(argv_array)
end
print_values
end
def parse_option_definitions(def_str)
a = def_str.split("\n")
@options=[]
while a[0]
if a[0] =~ /^\s*$/
a.shift
else
@options.push(Clop_Option.new(a))
end
end
end
def parse_command_line_options(argv_array)
while s = argv_array.shift
if s == "-h"
parse_help(argv_array, false)
exit
elsif s == "--help"
parse_help(argv_array, true)
exit
elsif i = find_option(s)
parse_option(i, s, argv_array)
else
raise "\n option \"#{s}\" not recognized; try \"-h\" or \"--help\"\n"
end
end
initialize_global_variables
end
def print_values
@options.each{|x| STDERR.print x.to_s}
end
def find_option(s)
i = nil
@options.each_index do |x|
i = x if s == @options[x].longname
if @options[x].shortname
i = x if s =~ Regexp.new(@options[x].shortname) and $` == ""
end
end
return i
end
def parse_option(i, s, argv_array)
if @options[i].type == "bool"
@options[i].valuestring = "true"
return
end
if s =~ /^-[^-]/ and (value = $') =~ /\w/
@options[i].valuestring = value
else
unless @options[i].valuestring = argv_array.shift
raise "\n option \"#{s}\" requires a value, but no value given;\n" +
" option description: #{@options[i].description}\n"
end
end
if @options[i].type =~ /^float\s*vector$/
while (@options[i].valuestring !~ /\]/)
@options[i].valuestring += " " + argv_array.shift
end
end
end
def initialize_global_variables
@options.each{|x| x.initialize_global_variable}
check_required_options
end
def check_required_options
options_missing = 0
@options.each do |x|
if x.valuestring == "none"
options_missing += 1
STDERR.print "option "
STDERR.print "\"#{x.shortname}\" or " if x.shortname
STDERR.print "\"#{x.longname}\" required. "
STDERR.print "Description:\n#{x.longdescription}\n"
end
end
if options_missing > 0
STDERR.print "Please provide the required command line option"
STDERR.print "s" if options_missing > 1
STDERR.print ".\n"
exit(1)
end
end
def parse_help(argv_array, long)
all = true
while s = argv_array.shift
if i = find_option(s)
all = false
print_help(long, i)
end
end
print_help(long) if all
end
def print_help(long, i = nil)
if i
STDERR.print help_string(@options[i], long)
else
@options.each{|x| STDERR.print help_string(x, long)}
end
end
def help_string(option, long_flag)
s = ""
if option.type
s += option_name_string(option)
end
if option.type or not long_flag
s += "#{option.description}"
s += default_value_string(option)
s += "\n"
end
if long_flag
s += "\n#{option.longdescription}\n"
end
return s
end
def option_name_string(option)
s = ""
if option.shortname
s += "#{option.shortname} "
end
s += "#{option.longname}"
s = option.add_tabs(s, s.size, 3)
s += ": "
return s
end
def default_value_string(option)
s = ""
if option.type and option.type != "bool"
reference_size = "#{option.description}".size + 2
s = option.add_tabs(s, reference_size, 4)
s += " [default: #{option.defaultvalue}]"
end
return s
end
end
def parse_command_line(def_str)
Clop.new(def_str, ARGV)
end
if __FILE__ == $0
options_definition_string = <<-END
Description: Command line option parser
Long description:
Test program for the class Clop (Command line option parser),
(c) 2004, Piet Hut and Jun Makino; see ACS at www.artcompsi.org
This program appears at the end of the file "clop.rb" that contains
the definition of the Clop class.
By running the file (typing "ruby clop.rb"), you can check whether
it still behaves correctly. Maximum help is provided by the command
"ruby clop.rb --help".
Short name: -s
Long name: --softening_length
Value type: float # double/real/...
Default value: 0.0
Global variable: eps # any comment allowed here
Description: Softening length # and here too
Long description: # and even here
This option sets the softening length used to calculate the force
between two particles. The calculation scheme comforms to standard
Plummer softening, where rs2=r**2+eps**2 is used in place of r**2.
Short name: -t
Long name: --end_time
Value type: float
Default value: 10
Global variable: t_end
Print name: t
Description: Time to stop integration
Long description:
This option gives the time to stop integration.
Short name: -n
Long name: --number_of_particles
Value type: int
Default value: none
Global variable: n_particles
Print name: N
Description: Number of particles
Long description:
Number of particles in an N-body snapshot.
Short name: -x
Long name: --extra_diagnostics
Value type: bool
Global variable: xdiag
Description: Extra diagnostics
Long description:
The following extra diagnostics will be printed:
acceleration (for all integrators)
jerk (for the Hermite integrator)
Short name: -v
Long name: --shift_velocity
Value type: float vector # numbers in between [ ] brackets
Default value: [3, 4, 5]
Global variable: vcom
Description: Shifts center of mass velocity
Long description:
The center of mass of the N-body system will be shifted by this amount.
If the vector has fewer components than the dimensionality of the N-body
system, zeroes will be added to the vector.
If the vector has more components than the dimensionality of the N-body
system, the extra components will be disgarded.
Short name: -o
Long name: --output_file_name
Value type: string
Default value: none
Global variable: output_file_name
Print name: # no name, hence name suppressed
Description: Name of the outputfile
Long description:
Name of the snapshot output file.
The snapshot contains the mass, position, and velocity values
for all particles in an N-body system.
Long name: --star_type # no short option given here
Value type: string
Default value: star: MS # parser cuts only at first ":"
Global variable: star_type
Description: Star type
Long description:
This options allows you to specify that a particle is a star, of a
certain type T, and possibly of subtypes t1, t2, ..., tk by specifying
--star_type "star: T: t1: t2: ...: tk". The ":" separators are allowed
to have blank spaces before and after them.
Examples: --star_type "star: MS"
--star_type "star : MS : ZAMS"
--star_type "star: giant: AGB"
--star_type "star:NS:pulsar:millisecond pulsar"
END
parse_command_line(options_definition_string)
end