-
Notifications
You must be signed in to change notification settings - Fork 52
/
Copy pathOption.v3
145 lines (140 loc) · 4.63 KB
/
Option.v3
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
// 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) {
def parse(val: string);
}
// An option that is tunable, e.g. from the command line.
class Option<T> extends Opt {
def init: T;
def parseFun: string -> T;
var onSetFun: T -> void;
var val: T;
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(prefix: string) {
def map = Strings.newMap<Opt>(); // maps names to options
var nullOpt: Opt; // an option with no name
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;
// Add {option} to the set of options.
def add<T>(option: Option<T>) -> Option<T> {
if (option.name == null) return nullOpt = option;
var name = option.name;
if (prefix != null) {
name = Strings.builderOf(prefix).putc('.').puts(name).toString();
}
map[name] = option;
return 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 command-line arguments {args}, setting options and returning remaining args.
def parse(args: Array<string>) -> Array<string> {
if (args == null) return [];
for (i < args.length) {
var arg = args[i];
if (arg.length == 0) continue;
if (arg[0] != '-') return Arrays.range(args, i, args.length);
parseOption(arg);
}
return [];
}
// Parse and set a single option from {arg}.
def parseOption(arg: string) -> bool {
var name: string, val: string;
for (i = 1; true; i++) {
if (i == arg.length) {
name = Arrays.range(arg, 1, arg.length);
break;
}
if (arg[i] == '=') {
name = Arrays.range(arg, 1, i);
val = Arrays.range(arg, i + 1, arg.length);
break;
}
}
return setOption(name, val);
}
// 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 {
names.put(name);
vals.put(val);
var option = if(name == null, nullOpt, map[name]);
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 {
new(prefix: string) : super(prefix) { }
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);
}
}