-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathParser.py
More file actions
175 lines (143 loc) · 4.37 KB
/
Copy pathParser.py
File metadata and controls
175 lines (143 loc) · 4.37 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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import re;
class ParseError(Exception):
def __init__(self, message):
self.message = message;
def __str__(self):
return self.message;
# makes a possibly negative or None key into a positive one
def regularize(key, start, stop, default = 0):
if key == None:
return default;
if key < 0:
return stop + key + 1;
return start + key;
# to make slicing long strings easier
# instead of creating a copy of the text, create a copy of the start pointer
class StringSlice:
def __init__(self, text, start = None, stop = None):
self.text = text;
self.start = regularize(start, 0, len(self.text), 0);
self.stop = regularize(stop, 0, len(self.text), len(self.text));
# force slicing
def __str__(self):
return self.text[self.start:self.stop];
def __eq__(self, other):
if isinstance(other, StringSlice):
return self.text == other.text and self.start == other.start and self.end == other.end;
else:
return self.text[self.start:self.stop] == other;
def __len__(self):
return int(self.stop - self.start);
def __getitem__(self, key):
# key should be a slice or an int
if isinstance(key, int):
# int: return character at that position (like a regular string)
return self.text[regularize(key, self.start, self.stop)];
elif isinstance(key, slice):
# slice: make a *_new_* StringSlice object (note the new!)
return StringSlice(self.text, regularize(key.start, self.start, self.stop, self.start), regularize(key.stop, self.start, self.stop, self.stop));
else:
raise TypeError("string indices must be integers or slices")
def __iter__(self):
for i in xrange(self.start, self.end):
yield self.text[i];
# returns a string, starting from the first non-whitespace character
whitespace = " \t\n\r#"
newlines = "\n\r"
def SkipWhitespace(text):
try:
while text[0] in whitespace:
# ignore comments
if text[0] == "#":
text = text[1:];
# take out everything up to a newline, which is taken care of by the regular whitespace removing
while text[0] not in newlines:
text = text[1:];
else:
text = text[1:];
except IndexError:
return "";
return text;
# checks that the text starts with the literal
def MatchText(literal):
def MatchLiteral(text):
text = SkipWhitespace(text);
if text[:len(literal)] == literal:
return (text[len(literal):], literal);
raise ParseError("Expected {}, received {}".format(literal, text[:len(literal)]));
# partial application!
return MatchLiteral;
# checks that the text starts with an integer
def MatchInt(text, signed = True):
text = SkipWhitespace(text);
startText = text[:16];
length = 0;
try:
# positive by default
negative = False;
if signed:
# check if the int is negative
if text[0] == "+":
text = text[1:];
elif text[0] == "-":
text = text[1:];
negative = True;
# actually make it into an int
number = 0;
while text[0] in '0123456789':
number *= 10;
number += int(text[0]);
text = text[1:];
length += 1;
except IndexError:
# end of file, so stop trying to parse
pass;
# make sure there are digits
if length == 0:
raise ParseError("Expected <int>, received {}".format(startText));
return text, ((-number) if negative else number);
# checks that the text starts with a float (or integer)
def MatchFloat(text):
text = SkipWhitespace(text);
startText = text; # note that StringSlices are immutable
end = 0;
# start with a integer part
if text[0] == '+':
text = text[1:];
elif text[0] == '-':
text = text[1:];
end += 1;
if text[0] not in '0123456789.':
raise ParseError("Expected <float>, received {}".format(text[:16]));
while text[0] in '0123456789':
end += 1;
text = text[1:];
# possibly add a fractional part
if text[0] == '.':
end += 1;
text = text[1:];
while text[0] in '0123456789':
end += 1;
text = text[1:];
# make it into a nice float
return text, float(str(startText[:end]));
# a name (used as key, so anything up to a ':' or ',')
def MatchName(text):
text = SkipWhitespace(text);
name = [];
try:
while text[0] not in ":,\n\r":
name.append(text[0]);
text = text[1:];
except IndexError:
pass;
if name == []:
raise ParseError("Expected <name>, received {}".format(text[:16]));
return text, "".join(name);
# matches a list of Match... functions
def ParseFormat(text, syntax):
result = [];
for element in syntax:
text, value = element(text);
result.append(value);
return text, result;