-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstring.py
More file actions
201 lines (163 loc) · 7.31 KB
/
Copy pathstring.py
File metadata and controls
201 lines (163 loc) · 7.31 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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
@namespace("string")
from Promethium import List
# A small, opt-in subset of Python's string module: just the character-class
# constants, exposed as zero-argument functions rather than Python-style
# module attributes (`string.digits` becomes `digits()`), for two confirmed
# reasons:
#
# - A top-level `name: str = "..."` module-level constant compiles fine on
# its own, but is unreachable from a consumer both ways: a bare `digits`
# doesn't resolve via `DefaultUses` the way a bare *function* call does
# (confirmed: `heapq`/`bisect`/`math`/`operator`/`itertools`'s functions
# all resolve bare via `DefaultUses` at the consumer level; a `str`-typed
# top-level constant declared the same way in this same namespace did
# not), and the fully-qualified form (`string.digits`) fails too — see
# the next point.
# - The namespace name `string` itself collides with the native `String`
# type: `string.digits` fails to compile with "Case for identifier
# 'string' does not match original case 'String'" followed by "No static
# member 'digits' on type 'String'" — the compiler resolves `string`
# case-insensitively to the platform's own `String` type instead of this
# module's namespace. Zero-argument functions sidestep this too, since
# consumers never need to write `string.` at all: add `string` to
# `DefaultUses` and call `digits()`, `punctuation()`, etc. bare, the same
# pattern already established for every other module in this project.
#
# `capwords` (below) needed confirming that native string methods are
# callable from Promethium at all — they are: `.ToUpper`/`.ToLower`/`.Trim`/
# `.Substring`/`.Length`/`.Replace`/`.Contains` all compile clean on every
# target via the same `defined("ECHOES"|"ISLAND"|"COOPER")`-branching
# pattern `math.py` uses for native calls (Cooper's spellings are the
# lowercase Java ones; Toffee's are the Foundation/NSString ones). `Split`
# is the one exception found so far: neither a bare string argument nor a
# `Char[]` argument matched a single confirmed overload cleanly on a first
# attempt, so `capwords` deliberately avoids it, splitting words with a
# manual character scan (`_substring`/`_length` only) instead. `Template`
# is still not attempted — it needs far more string-parsing machinery than
# `capwords`' single split-and-capitalize pass.
def _upper(value: str) -> str:
if defined("ECHOES") or defined("ISLAND"):
return value.ToUpper()
elif defined("COOPER"):
return value.toUpperCase()
else:
return value.uppercaseString
def _lower(value: str) -> str:
if defined("ECHOES") or defined("ISLAND"):
return value.ToLower()
elif defined("COOPER"):
return value.toLowerCase()
else:
return value.lowercaseString
def _length(value: str) -> int:
if defined("COOPER") or defined("TOFFEE"):
return value.length()
else:
return value.Length
def _substring(value: str, start: int, count: int) -> str:
if defined("ECHOES") or defined("ISLAND"):
return value.Substring(start, count)
elif defined("COOPER"):
return value.substring(start, start + count)
else:
return value.substringWithRange(NSMakeRange(start, count))
def _capitalizeWord(word: str) -> str:
if _length(word) == 0:
return word
first: str = _upper(_substring(word, 0, 1))
rest: str = _lower(_substring(word, 1, _length(word) - 1))
return first + rest
def capwords(value: str) -> str:
result: str = ""
wordStart: int = 0
length: int = _length(value)
index: int = 0
while index <= length:
atBoundary: bool = False
if index == length:
atBoundary = True
elif _substring(value, index, 1) == " ":
atBoundary = True
if atBoundary:
if index > wordStart:
word: str = _substring(value, wordStart, index - wordStart)
if _length(result) > 0:
result += " "
result += _capitalizeWord(word)
wordStart = index + 1
index += 1
return result
def _isAsciiWhitespace(ch: str) -> bool:
return ch == " " or ch == "\t" or ch == "\n" or ch == "\r" or ch == "\v" or ch == "\f"
def _startsWithAt(value: str, valueLength: int, needle: str, needleLength: int, index: int) -> bool:
if needleLength == 0 or index + needleLength > valueLength:
return False
return _substring(value, index, needleLength) == needle
# `str.split`, standing in for native `String.Split` — the one native string
# method that turned out to have no single confirmed overload from
# Promethium (bare string argument and `Char[]` argument each matched a
# *different* unwanted overload; see the "String.Split's overload set"
# compiler-gaps entry in the stdlib survey). The compiler team's own call:
# not a Promethium parser gap, just a genuinely confusing overload set not
# worth distorting native resolution for — so this is a plain BaseLibrary-
# level implementation instead, the same manual character-scan idiom
# `capwords` already uses.
#
# `sep: str = None` mirrors Python's own `str.split(sep=None)` signature:
# omitting `sep` (or passing `None`) splits on runs of whitespace and
# discards leading/trailing/empty tokens, matching CPython's `" a b "
# .split()` behavior exactly. Passing a non-empty `sep` splits on that
# literal substring and *keeps* empty tokens between consecutive
# separators (`"a,,b".split(",") == ["a", "", "b"]`), also matching
# CPython. An empty `sep` is a `ValueError` in CPython; this has no
# exception-raising convention established anywhere else in this project,
# so it degrades to returning `[value]` unchanged instead.
def split(value: str, sep: str = None) -> List[str]:
result: List[str] = List[str]()
length: int = _length(value)
if sep is None:
tokenStart: int = -1
index: int = 0
while index <= length:
atSeparator: bool = index == length or _isAsciiWhitespace(_substring(value, index, 1))
if atSeparator:
if tokenStart >= 0:
result.append(_substring(value, tokenStart, index - tokenStart))
tokenStart = -1
elif tokenStart < 0:
tokenStart = index
index += 1
return result
sepLength: int = _length(sep)
if sepLength == 0:
result.append(value)
return result
tokenStart: int = 0
index: int = 0
while index <= length - sepLength:
if _startsWithAt(value, length, sep, sepLength, index):
result.append(_substring(value, tokenStart, index - tokenStart))
index += sepLength
tokenStart = index
else:
index += 1
result.append(_substring(value, tokenStart, length - tokenStart))
return result
def ascii_lowercase() -> str:
return "abcdefghijklmnopqrstuvwxyz"
def ascii_uppercase() -> str:
return "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def ascii_letters() -> str:
return ascii_lowercase() + ascii_uppercase()
def digits() -> str:
return "0123456789"
def hexdigits() -> str:
return "0123456789abcdefABCDEF"
def octdigits() -> str:
return "01234567"
def punctuation() -> str:
return "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
def whitespace() -> str:
return " \t\n\r"
def printable() -> str:
return digits() + ascii_letters() + punctuation() + whitespace()