-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutil.d
149 lines (117 loc) · 4.39 KB
/
util.d
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
/********************************************************************************
* util.d: assorted utility functions *
* 2016 - Ben Perlin *
*******************************************************************************/
import std.ascii;
import std.format;
import std.range;
import std.string : chomp;
import std.traits;
struct Token
{
string str;
uint column;
}
char[] carrotLine(const uint columnNumber) {return ' '.repeat(columnNumber).array ~ '^';}
string pointOutError(const Token errorToken, const string line) {
return format("%s\n%s", line.chomp(), carrotLine(errorToken.column));
}
void formatException(Char, Args...)(const Char[] fmt, Args args)
if (isSomeChar!Char)
{
throw new Exception(format(fmt, args));
}
/* Provides standardized error handling
* Introduced late, not used everywhere */
string errorString(string error, Token token, string line, uint lineNumber) {
return format("Error on line %d: %s\n\n%s",
lineNumber, error, pointOutError(token, line));
}
long parseIntegerToken(Token token, uint bitWidth, bool signed, string line, uint lineNumber)
in {
import std.string;
assert(bitWidth < 60); /* ensures no overflow is possable under any circumstances */
assert(token.str.length >= 1);
assert(indexOf(token.str, '-', 1) == -1); /* negative sign may only appear in first column */
} body {
void parseError() {
throw new Exception(errorString("failed to parse numeric token",
token, line, lineNumber));
}
bool negative = (token.str[0] == '-');
if (negative && !signed) {
throw new Exception(errorString("negative token not accepted by unsigned feild",
token, line, lineNumber));
}
string numeralToken = token.str[negative .. $];
long base = 10;
switch (numeralToken[0]) {
case '0':
if (numeralToken.length == 1) return 0;
else if (numeralToken[1].toLower() == 'x') {
base = 16;
if (numeralToken.length == 2) parseError();
numeralToken = numeralToken[2 .. $];
} else {
base = 8;
numeralToken = numeralToken[1 .. $];
}
break;
case '1': .. case '9':
base = 10;
break;
default: parseError();
}
long intermediate;
parse:
foreach (c; numeralToken) {
long digit;
char cu = c.toUpper();
switch (cu) {
case '0': .. case '9':
digit = cu - '0';
break;
case 'A': .. case 'F':
digit = 0xA + cu - 'A';
break;
case '_': continue parse;
default: parseError();
}
if (digit >= base) parseError();
intermediate = intermediate*base + digit;
}
intermediate *= negative ? -1 : 1;
if (signed ? intermediate < -(1L<<(bitWidth-1)) || intermediate >= (1<<(bitWidth-1))
: intermediate < 0 || intermediate >= (1UL<<bitWidth)) {
throw new Exception(errorString(format("numeric token out of range for %d bit %s integer",
bitWidth, (signed ? "signed" : "unsigned")),
token, line, lineNumber));
}
return intermediate;
}
/* wrapper for parseIntegerToken to parse start address from command line */
uint parseStartAddress(string startAddressString) {
return cast(uint) parseIntegerToken(Token(startAddressString, 0), 32, false,
startAddressString, 0);
}
unittest {
/* does not test error handling, messages will be incorrectly */
void runTest(string token, uint bitWidth, bool signed, long expected) {
long output = parseIntegerToken(Token(token, 0), bitWidth, signed, token, 0);
assert(output == expected, format("ASSERTION: integer token \"%s\" parses to %d, expected %d",
token, output, expected));
}
runTest("0", 5, false, 0);
runTest("00", 5, false, 0);
runTest("0x0", 5, false, 0);
runTest("0", 16, true, 0);
runTest("00", 16, true, 0);
runTest("0x0", 16, true, 0);
runTest("42", 26, false, 42);
runTest("052", 26, false, 42);
runTest("0x2A", 26, false, 42);
runTest("42", 16, true, 42);
runTest("052", 16, true, 42);
runTest("0x2A", 16, true, 42);
// todo test negatives and overflows
}