parent
d15f63a0f2
commit
5a58440ebe
3 changed files with 102 additions and 1 deletions
|
|
@ -56,6 +56,7 @@
|
||||||
- `writeStackTrace` is available in JS backend now.
|
- `writeStackTrace` is available in JS backend now.
|
||||||
|
|
||||||
- `strscans.scanf` now supports parsing single characters.
|
- `strscans.scanf` now supports parsing single characters.
|
||||||
|
- `strscans.scanTuple` added which uses `strscans.scanf` internally, returning a tuple which can be unpacked for easier usage of `scanf`.
|
||||||
|
|
||||||
|
|
||||||
- Added `math.isNaN`.
|
- Added `math.isNaN`.
|
||||||
|
|
|
||||||
|
|
@ -284,6 +284,7 @@ efficiency and perform different checks.
|
||||||
|
|
||||||
|
|
||||||
import macros, parseutils
|
import macros, parseutils
|
||||||
|
import std/private/since
|
||||||
|
|
||||||
proc conditionsToIfChain(n, idx, res: NimNode; start: int): NimNode =
|
proc conditionsToIfChain(n, idx, res: NimNode; start: int): NimNode =
|
||||||
assert n.kind == nnkStmtList
|
assert n.kind == nnkStmtList
|
||||||
|
|
@ -465,6 +466,54 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
|
||||||
else:
|
else:
|
||||||
result.add res
|
result.add res
|
||||||
|
|
||||||
|
macro scanTuple*(input: untyped; pattern: static[string]; matcherTypes: varargs[untyped]): untyped {.since: (1, 5).}=
|
||||||
|
## Works identically as scanf, but instead of predeclaring variables it returns a tuple.
|
||||||
|
## Tuple is started with a bool which indicates if the scan was successful
|
||||||
|
## followed by the requested data.
|
||||||
|
## If using a user defined matcher, provide the types in order they appear after pattern:
|
||||||
|
## `line.scanTuple("${yourMatcher()}", int)`
|
||||||
|
runnableExamples:
|
||||||
|
let (success, year, month, day, time) = scanTuple("1000-01-01 00:00:00", "$i-$i-$i$s$+")
|
||||||
|
if success:
|
||||||
|
assert year == 1000
|
||||||
|
assert month == 1
|
||||||
|
assert day == 1
|
||||||
|
assert time == "00:00:00"
|
||||||
|
var
|
||||||
|
p = 0
|
||||||
|
userMatches = 0
|
||||||
|
arguments: seq[NimNode]
|
||||||
|
result = newStmtList()
|
||||||
|
template addVar(typ: string) =
|
||||||
|
let varIdent = ident("temp" & $arguments.len)
|
||||||
|
result.add(newNimNode(nnkVarSection).add(newIdentDefs(varIdent, ident(typ), newEmptyNode())))
|
||||||
|
arguments.add(varIdent)
|
||||||
|
while p < pattern.len:
|
||||||
|
if pattern[p] == '$':
|
||||||
|
inc p
|
||||||
|
case pattern[p]
|
||||||
|
of 'w', '*', '+':
|
||||||
|
addVar("string")
|
||||||
|
of 'c':
|
||||||
|
addVar("char")
|
||||||
|
of 'b', 'o', 'i', 'h':
|
||||||
|
addVar("int")
|
||||||
|
of 'f':
|
||||||
|
addVar("float")
|
||||||
|
of '{':
|
||||||
|
if userMatches < matcherTypes.len:
|
||||||
|
let varIdent = ident("temp" & $arguments.len)
|
||||||
|
result.add(newNimNode(nnkVarSection).add(newIdentDefs(varIdent, matcherTypes[userMatches], newEmptyNode())))
|
||||||
|
arguments.add(varIdent)
|
||||||
|
inc userMatches
|
||||||
|
else: discard
|
||||||
|
inc p
|
||||||
|
result.add newPar(newCall(ident("scanf"), input, newStrLitNode(pattern)))
|
||||||
|
for arg in arguments:
|
||||||
|
result[^1][0].add arg
|
||||||
|
result[^1].add arg
|
||||||
|
result = newBlockStmt(result)
|
||||||
|
|
||||||
template atom*(input: string; idx: int; c: char): bool =
|
template atom*(input: string; idx: int; c: char): bool =
|
||||||
## Used in scanp for the matching of atoms (usually chars).
|
## Used in scanp for the matching of atoms (usually chars).
|
||||||
## EOF is matched as ``'\0'``.
|
## EOF is matched as ``'\0'``.
|
||||||
|
|
@ -639,4 +688,4 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
|
||||||
result.add toIfChain(conds, idx, res, 0)
|
result.add toIfChain(conds, idx, res, 0)
|
||||||
result.add res
|
result.add res
|
||||||
when defined(debugScanp):
|
when defined(debugScanp):
|
||||||
echo repr result
|
echo repr result
|
||||||
|
|
@ -227,3 +227,54 @@ block:
|
||||||
if line.scanf("$i-$i $c: $w", lo, hi, c, w):
|
if line.scanf("$i-$i $c: $w", lo, hi, c, w):
|
||||||
inc res
|
inc res
|
||||||
doAssert res == 4
|
doAssert res == 4
|
||||||
|
|
||||||
|
block:
|
||||||
|
#whenscanf testing
|
||||||
|
let input = """1-3 s: abc
|
||||||
|
15-18 9: def
|
||||||
|
15-18 A: ghi
|
||||||
|
15-18 _: jkl
|
||||||
|
"""
|
||||||
|
proc twoDigits(input: string; x: var int; start: int): int =
|
||||||
|
if start+1 < input.len and input[start] == '0' and input[start+1] == '0':
|
||||||
|
result = 2
|
||||||
|
x = 13
|
||||||
|
else:
|
||||||
|
result = 0
|
||||||
|
|
||||||
|
proc someSep(input: string; start: int; seps: set[char] = {';', ',', '-', '.'}): int =
|
||||||
|
result = 0
|
||||||
|
while start+result < input.len and input[start+result] in seps: inc result
|
||||||
|
|
||||||
|
var res = 0
|
||||||
|
for line in input.splitLines:
|
||||||
|
let (success, lo, hi, c ,w) = scanTuple(line, "$i-$i $c: $w")
|
||||||
|
if success:
|
||||||
|
inc res
|
||||||
|
doAssert res == 4
|
||||||
|
|
||||||
|
let (_, key, val, intval, floatVal) = scanTuple("abc:: xyz 89 33.25", "$w$s::$s$w$s$i $f")
|
||||||
|
doAssert key == "abc"
|
||||||
|
doAssert val == "xyz"
|
||||||
|
doAssert intval == 89
|
||||||
|
doAssert floatVal == 33.25
|
||||||
|
|
||||||
|
|
||||||
|
let (_, binVal, octVal, hexVal) = scanTuple("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binval, octval, hexval)
|
||||||
|
doAssert binval == 0b0101
|
||||||
|
doAssert octval == 0o1234
|
||||||
|
doAssert hexval == 0xabcd
|
||||||
|
|
||||||
|
var (xx,_) = scanTuple("$abc", "$$$i")
|
||||||
|
doAssert xx == false
|
||||||
|
|
||||||
|
|
||||||
|
let (xx2, _) = block: scanTuple("$1234", "$$$i")
|
||||||
|
doAssert xx2
|
||||||
|
|
||||||
|
var (yy, intval2, key2) = scanTuple(";.--Breakpoint00 [output]",
|
||||||
|
"$[someSep]Breakpoint${twoDigits}$[someSep({';','.','-'})] [$+]$.",
|
||||||
|
int)
|
||||||
|
doAssert yy
|
||||||
|
doAssert key2 == "output"
|
||||||
|
doAssert intVal2 == 13
|
||||||
Loading…
Add table
Add a link
Reference in a new issue