-
Notifications
You must be signed in to change notification settings - Fork 61
Expand file tree
/
Copy pathOption.v3
More file actions
155 lines (150 loc) · 5.3 KB
/
Option.v3
File metadata and controls
155 lines (150 loc) · 5.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Copyright 2010 Google Inc. All rights reserved.
// See LICENSE for details of Apache 2.0 license.
// Base class of {Option} that hides the type variable in order to allow
// options to be put in collections.
class Opt(name: string) {
var consumeNext: bool; // if true, consume the next argument
def parse(val: string);
}
// An option that is tunable, e.g. from the command line.
class Option<T> extends Opt {
def init: T; // initial (or default) value
def parseFun: string -> T; // parse function
var onSetFun: T -> void; // optional action to take to set value
var val: T; // current value
new(name: string, init, parseFun) : super(name) { val = init; }
def parse(val: string) {
this.val = parseFun(val);
if (onSetFun != null) onSetFun(this.val);
}
def onSet(f: T -> void) -> this { this.onSetFun = f; }
def get() -> T { return val; }
}
// The Options class represents a collection of options, each with a name and a value.
// Any option that has been set to a value other than its default is also remembered.
class Options {
def map = Strings.newMap<Opt>(); // maps names to options
var names = Vector<string>.new(); // the names of all parsed options
var vals = Vector<string>.new(); // the values of all options
var setUnmatched: (string, string) -> void;
var numHyphens = 1; // number of hyphens starting option
// Add {option} to the set of options.
def add<T>(option: Option<T>) -> Option<T> {
return map[option.name] = option;
}
// Load the given option values in {from} into this option set.
def load(from: Options) {
var max = from.names.length;
for (i < max) setOption(from.names[i], from.vals[i]);
}
// Parse {args}, considering arguments beginning with {numHyphens} '-' to be options, updating
// their internal values. Returns the remaining arguments after options.
def parse(args: Array<string>) -> Array<string> {
if (args == null) return [];
var i = 0;
while (i < args.length) {
var arg = args[i];
if (!isOptionPrefix(arg)) break;
// Parse argument into (name, value) pair
var t = parseOption(arg), name = t.0, val = t.1;
var option = map[name];
// If the option consumes the next arg, and there is one, consume it.
if (option != null && option.consumeNext && val == null && i < (args.length - 1)) {
var n = args[i + 1];
if (!isOptionPrefix(n)) { // check if the next arg is an option
val = n;
i++;
}
}
setOption0(name, val, option);
i++;
}
return Arrays.range(args, i, args.length);
}
// Parse an option "-name[=value]" into the name and value parts.
def parseOption(arg: string) -> (string, string) {
for (i = numHyphens; i < arg.length; i++) {
if (arg[i] == '=') {
var name = Arrays.range(arg, numHyphens, i);
var val = Arrays.range(arg, i + 1, arg.length);
return (name, val);
}
}
var name = Arrays.range(arg, numHyphens, arg.length);
return (name, null);
}
// Set the option with name {name} to a given value {val}. If {name == null}, then
// the {nullOpt} will be set. Returns {true} if the option was successfully set.
def setOption(name: string, val: string) -> bool {
return setOption0(name, val, map[name]);
}
def isOptionPrefix(str: string) -> bool {
if (str.length < numHyphens) return false;
for (i < numHyphens) if (str[i] != '-') return false;
return true;
}
private def setOption0(name: string, val: string, option: Opt) -> bool {
names.put(name);
vals.put(val);
if (option != null) {
option.parse(val);
return true;
}
if (setUnmatched != null) setUnmatched(name, val);
return false;
}
}
// BasicOptions adds a set of utility methods for adding and parsing options
// of type bool, int, and string.
class BasicOptions extends Options {
def newIntOption(name: string, val: int) -> Option<int> {
return add(Option.new(name, val, parseInt));
}
def newSizeOption(name: string, val: u32) -> Option<u32> {
return add(Option.new(name, val, parseSize));
}
def newAddrOption(name: string, val: u64) -> Option<u64> {
return add(Option.new(name, val, parseAddr));
}
def newBoolOption(name: string, val: bool) -> Option<bool> {
return add(Option.new(name, val, parseBool));
}
def newStringOption(name: string, val: string) -> Option<string> {
return add(Option.new(name, val, parseString));
}
def newOption<T>(name: string, val: T, parseFun: string -> T) -> Option<T> {
return add(Option.new(name, val, parseFun));
}
def parseBool(str: string) -> bool {
return str == null || Strings.equal(str, "true");
}
def parseInt(str: string) -> int {
var p = Ints.parseDecimal(str, 0);
return if(p.0 > 0, p.1);
}
// Parse a size, allowing suffixes such as K, M, and G.
def parseSize(str: string) -> u32 {
var len = str.length;
var last = str[len - 1], scale = 1u, max = u32.max;
match(last) {
'k', 'K' => { scale = 1024u; max = 4194304u; len--; }
'm', 'M' => { scale = 1048576u; max = 4096u; len--; }
'g', 'G' => { scale = 1073741824u; max = 4u; len--; }
}
var p = Ints.parsePosDecimal(str, 0);
if (p.0 == len) {
if (p.1 >= max) return u32.max;
return p.1 * scale;
}
return 0;
}
// Parse an address, which is a hexadecimal number starting with "0x".
def parseAddr(str: string) -> u64 {
var len = str.length;
var p = Longs.parse0xHex(str, 0);
return if(p.0 > 0, u64.view(p.1));
}
def parseString(str: string) -> string {
return if(str == null, "", str);
}
}