-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WIP] fix #nim-lang/RFCs/issues/84 generic parse
- Loading branch information
1 parent
2039dad
commit b313e67
Showing
1 changed file
with
113 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
#[ | ||
unfinished | ||
generic parser | ||
]# | ||
|
||
import parseutils | ||
from strutils import startsWith, continuesWith | ||
|
||
template enforce(a: untyped, msg: untyped) = | ||
# todo: custom catchable exception | ||
doAssert a, $msg | ||
|
||
proc skipWhitespace(src: string, start: var int) = | ||
while start < src.len and src[start] == ' ': # CHECKME for ws | ||
start.inc | ||
|
||
template skipWhitespace2() = skipWhitespace(src, start) | ||
|
||
template continuesWith2(pattern: string): bool= | ||
src.continuesWith(pattern, start) | ||
|
||
|
||
template continuesWithAndAdvance(pattern: string): bool= | ||
let old = start | ||
skipWhitespace2() | ||
if src.continuesWith(pattern, start): | ||
start.inc pattern.len | ||
true | ||
else: | ||
start = old | ||
false | ||
|
||
proc eat(src: string, start: var int, pattern: string) = | ||
enforce continuesWithAndAdvance(pattern), (src, start, pattern) | ||
|
||
template eat2(pattern: string) = eat(src, start, pattern) | ||
|
||
proc parseFrom*[T](dst: var T, src: string, start: var int) = | ||
template process(parseCustom) = | ||
let ret = parseCustom(src, dst, start) | ||
enforce ret > 0, (start, src) | ||
start += ret | ||
|
||
skipWhitespace2() | ||
when T is seq: | ||
parseFromList(dst, src, start, "@[", "]") | ||
elif T is array: | ||
parseFromList(dst, src, start, "[", "]") | ||
elif T is tuple: | ||
parseFromTuple(dst, src, start, "(", ")") | ||
elif T is int: process(parseInt) | ||
# elif T is string: process(parseString) # todo | ||
elif T is float: process(parseFloat) | ||
elif T is bool: | ||
if continuesWithAndAdvance("true"): dst = true | ||
elif continuesWithAndAdvance("false"): dst = false | ||
else: | ||
# else: enforce false, ($T, start, src) # nim BUG cgen error | ||
doAssert false, $($T, start) | ||
else: | ||
static: doAssert false, $T | ||
|
||
proc parseFromTuple*(dst: var tuple, src: string, start: var int, left, right: string) = | ||
eat2(left) | ||
var done = false | ||
for k,v in fieldPairs(dst): | ||
enforce not done, (src, start, k) | ||
parseFrom(v, src, start) | ||
if not continuesWithAndAdvance(","): | ||
done = true | ||
eat2(right) | ||
|
||
type seqLike = seq | array | ||
proc parseFromList*(dst: var seqLike, src: string, start: var int, left, right: string) = | ||
type T = type(dst[0]) #CHECKME: ok for empty array? | ||
eat2(left) | ||
var count = 0 | ||
while true: | ||
if continuesWithAndAdvance(right): return | ||
var a: T | ||
parseFrom(a, src, start) | ||
when dst is seq: | ||
dst.add a | ||
elif dst is array: | ||
dst[count] = a | ||
count.inc | ||
else: | ||
static: doAssert false | ||
if continuesWithAndAdvance(","): | ||
continue | ||
else: | ||
break | ||
eat2(right) | ||
|
||
proc parseFrom*[T](dst: var T, src: string) = | ||
var start = 0 | ||
parseFrom(dst, src, start) | ||
enforce start == src.len, (start, src.len) | ||
|
||
proc parse*[T](src: string): T = | ||
parseFrom(result, src) | ||
|
||
when isMainModule: | ||
proc runTest[T](a: T) = | ||
doAssert a == parse[T]($a), $(a, $a, $T) | ||
runTest 123 | ||
runTest 3.14 | ||
runTest @[1,2] | ||
runTest @[@[1,2], @[3], @[]] | ||
runTest @[true, false] | ||
runTest [true, false] | ||
runTest (true, 12, 3.4) | ||
runTest (true, [12.2], (3.4,32,)) |