Skip to content

Commit

Permalink
Add push raises (#101)
Browse files Browse the repository at this point in the history
* Add push raises

* Fix gcsafe violation

* remove debug code
  • Loading branch information
jangko committed Feb 12, 2024
1 parent 7340359 commit 57ff0b8
Show file tree
Hide file tree
Showing 19 changed files with 111 additions and 42 deletions.
11 changes: 9 additions & 2 deletions confutils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const

when not defined(nimscript):
import
os, terminal,
terminal,
confutils/shell_completion

type
Expand Down Expand Up @@ -70,6 +70,8 @@ const
confutils_description_width {.intdefine.} = 80
confutils_narrow_terminal_width {.intdefine.} = 36

{.push gcsafe, raises: [].}

func getFieldName(caseField: NimNode): NimNode =
result = caseField
if result.kind == nnkIdentDefs: result = result[0]
Expand Down Expand Up @@ -565,7 +567,10 @@ proc parseCmdArgAux(T: type, s: string): T {.raises: [ValueError].} =
# If you have provided your own specializations, please handle
# all other exception types.
mixin parseCmdArg
parseCmdArg(T, s)
try:
parseCmdArg(T, s)
except CatchableError as exc:
raise newException(ValueError, exc.msg)

func completeCmdArg*(T: type enum, val: string): seq[string] =
for e in low(T)..high(T):
Expand Down Expand Up @@ -1291,3 +1296,5 @@ func load*(f: TypedInputFile): f.ContentType =
else:
mixin loadFile
loadFile(f.Format, f.string, f.ContentType)

{.pop.}
3 changes: 3 additions & 0 deletions confutils/cli_parser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ type
## or the argument, and the value is not "" if
## the option was given a value

{.push gcsafe, raises: [].}

func parseWord(s: string, i: int, w: var string,
delim: set[char] = {'\t', ' '}): int =
result = i
Expand Down Expand Up @@ -161,3 +163,4 @@ iterator getopt*(cmds: seq[string],
if p.kind == cmdEnd: break
yield (p.kind, p.key, p.val)

{.pop.}
3 changes: 3 additions & 0 deletions confutils/cli_parsing_fuzzer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import
stew/byteutils, testutils/fuzzing,
../confutils

{.push gcsafe, raises: [].}

template fuzzCliParsing*(Conf: type) =
test:
block:
Expand All @@ -22,3 +24,4 @@ template fuzzCliParsing*(Conf: type) =
except ConfigurationError as err:
discard

{.pop.}
25 changes: 16 additions & 9 deletions confutils/config_file.nim
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ type

OriginalToGeneratedFields = OrderedTable[string, GeneratedFieldInfo]

{.push gcsafe, raises: [].}

func isOption(n: NimNode): bool =
if n.kind != nnkBracketExpr: return false
eqIdent(n[0], "Option")
Expand Down Expand Up @@ -248,19 +250,20 @@ proc generateTypes(root: ConfFileSection): seq[NimNode] =
recList.add generateOptionalField(child.getRenamedName.ident, child.typ)
result[index].putRecList(recList)

proc generateSettersPaths(node: ConfFileSection, result: var OriginalToGeneratedFields) =
var path {.global.}: seq[string]
path.add node.getRenamedName
proc generateSettersPaths(node: ConfFileSection,
result: var OriginalToGeneratedFields,
pathsCache: var seq[string]) =
pathsCache.add node.getRenamedName
if node.children.len == 0:
result[node.fieldName] = (node.isCommandOrArgument, path)
result[node.fieldName] = (node.isCommandOrArgument, pathsCache)
else:
for child in node.children:
generateSettersPaths(child, result)
path.del path.len - 1
generateSettersPaths(child, result, pathsCache)
pathsCache.del pathsCache.len - 1

proc generateSettersPaths(root: ConfFileSection): OriginalToGeneratedFields =
proc generateSettersPaths(root: ConfFileSection, pathsCache: var seq[string]): OriginalToGeneratedFields =
for child in root.children:
generateSettersPaths(child, result)
generateSettersPaths(child, result, pathsCache)

template cfSetter(a, b: untyped): untyped =
when a is Option:
Expand Down Expand Up @@ -346,9 +349,13 @@ macro generateSecondarySources*(ConfType: type): untyped =
let
model = generateConfigFileModel(ConfType)
modelType = generateTypes(model)
var
pathsCache: seq[string]

result = newTree(nnkStmtList)
result.add newTree(nnkTypeSection, modelType)

let settersPaths = model.generateSettersPaths
let settersPaths = model.generateSettersPaths(pathsCache)
result.add generateConfigFileSetters(ConfType, result[^1], settersPaths)

{.pop.}
4 changes: 4 additions & 0 deletions confutils/defs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ type

SomeDistinctString = InputFile|InputDir|OutPath|OutDir|OutFile

{.push gcsafe, raises: [].}

template `/`*(dir: InputDir|OutDir, path: string): auto =
string(dir) / path

Expand All @@ -70,3 +72,5 @@ template implicitlySelectable* {.pragma.}
## to allow the value of the discriminator to be determined
## implicitly when the user specifies any of the sub-options
## that depend on the disciminator value.

{.pop.}
28 changes: 19 additions & 9 deletions confutils/shell_completion.nim
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,21 @@ const
WORDBREAKS = "\"'@><=;|&(:"
SAFE_CHARS = {'a'..'z', 'A'..'Z', '0'..'9', '@', '%', '+', '=', ':', ',', '.', '/', '-'}

proc open(l: var ShellLexer, input: Stream, wordBreakChars: string = WORDBREAKS, preserveTrailingWs = true) =
{.push gcsafe, raises: [].}

proc open(l: var ShellLexer,
input: Stream,
wordBreakChars: string = WORDBREAKS,
preserveTrailingWs = true) {.gcsafe, raises: [IOError, OSError].} =
lexbase.open(l, input)
l.preserveTrailingWs = preserveTrailingWs
l.mergeWordBreaks = false
l.wordBreakChars = wordBreakChars

proc parseQuoted(l: var ShellLexer, pos: int, isSingle: bool, output: var string): int =
proc parseQuoted(l: var ShellLexer,
pos: int,
isSingle: bool,
output: var string): int {.gcsafe, raises: [IOError, OSError].} =
var pos = pos
while true:
case l.buf[pos]:
Expand Down Expand Up @@ -62,7 +70,7 @@ proc parseQuoted(l: var ShellLexer, pos: int, isSingle: bool, output: var string
inc(pos)
return pos

proc getTok(l: var ShellLexer): Option[string] =
proc getTok(l: var ShellLexer): Option[string] {.gcsafe, raises: [IOError, OSError].} =
var pos = l.bufpos

# Skip the initial whitespace
Expand Down Expand Up @@ -179,6 +187,8 @@ proc shellPathEscape*(path: string): string =
result.add('\\')
result.add(ch)

{.pop.}

when isMainModule:
# Test data lifted from python's shlex unit-tests
const data = """
Expand Down Expand Up @@ -265,9 +275,9 @@ foo\ bar|foo bar|
echo "expected ", expected
doAssert(false)

doAssert(quoteWord("") == "''")
doAssert(quoteWord("\\\"") == "'\\\"'")
doAssert(quoteWord("foobar") == "foobar")
doAssert(quoteWord("foo$bar") == "'foo$bar'")
doAssert(quoteWord("foo bar") == "'foo bar'")
doAssert(quoteWord("foo'bar") == "'foo\\'bar'")
doAssert(shellQuote("") == "''")
doAssert(shellQuote("\\\"") == "'\\\"'")
doAssert(shellQuote("foobar") == "foobar")
doAssert(shellQuote("foo$bar") == "'foo$bar'")
doAssert(shellQuote("foo bar") == "'foo bar'")
doAssert(shellQuote("foo'bar") == "'foo\\'bar'")
8 changes: 6 additions & 2 deletions confutils/std/net.nim
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ import std/net
from std/parseutils import parseInt
export net

func parseCmdArg*(T: type IpAddress, s: string): T =
{.push gcsafe, raises: [].}

func parseCmdArg*(T: type IpAddress, s: string): T {.gcsafe, raises: [ValueError].} =
parseIpAddress(s)

func completeCmdArg*(T: type IpAddress, val: string): seq[string] =
# TODO: Maybe complete the local IP address?
@[]

func parseCmdArg*(T: type Port, s: string): T =
func parseCmdArg*(T: type Port, s: string): T {.gcsafe, raises: [ValueError].} =
template fail =
raise newException(ValueError,
"The supplied port must be an integer value in the range 1-65535")
Expand All @@ -34,3 +36,5 @@ func parseCmdArg*(T: type Port, s: string): T =

func completeCmdArg*(T: type Port, val: string): seq[string] =
@[]

{.pop.}
4 changes: 4 additions & 0 deletions confutils/toml/defs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import
export
toml_serialization, confutilsDefs

{.push gcsafe, raises: [].}

template readConfutilsType(T: type) =
template readValue*(r: var TomlReader, val: var T) =
val = T r.readValue(string)
Expand All @@ -22,3 +24,5 @@ readConfutilsType InputDir
readConfutilsType OutPath
readConfutilsType OutDir
readConfutilsType OutFile

{.pop.}
14 changes: 9 additions & 5 deletions confutils/toml/std/net.nim
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@ import
export
net, toml_serialization

{.push gcsafe, raises: [].}

proc readValue*(r: var TomlReader, val: var IpAddress)
{.raises: [SerializationError, IOError, Defect].} =
{.raises: [SerializationError, IOError].} =
val = try: parseIpAddress(r.readValue(string))
except ValueError as err:
r.lex.raiseUnexpectedValue("IP address")
r.lex.raiseUnexpectedValue("IP address " & err.msg)

proc readValue*(r: var TomlReader, val: var Port)
{.raises: [SerializationError, IOError, Defect].} =
{.raises: [SerializationError, IOError].} =
let port = try: r.readValue(uint16)
except ValueError:
r.lex.raiseUnexpectedValue("Port")
except ValueError as exc:
r.lex.raiseUnexpectedValue("Port " & exc.msg)

val = Port port

{.pop.}
7 changes: 5 additions & 2 deletions confutils/toml/std/uri.nim
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ import
export
uri, toml_serialization

{.push gcsafe, raises: [].}

proc readValue*(r: var TomlReader, val: var Uri)
{.raises: [SerializationError, IOError, Defect].} =
{.raises: [SerializationError, IOError].} =
val = try: parseUri(r.readValue(string))
except ValueError as err:
r.lex.raiseUnexpectedValue("URI")
r.lex.raiseUnexpectedValue("URI " & err.msg)

{.pop.}
11 changes: 8 additions & 3 deletions confutils/winreg/reader.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import
tables, typetraits, options,
serialization/[object_serialization],
serialization/[object_serialization, errors],
./utils, ./types

type
Expand All @@ -24,11 +24,13 @@ type
deserializedField*: string
innerException*: ref CatchableError

{.push gcsafe, raises: [].}

proc handleReadException*(r: WinregReader,
Record: type,
fieldName: string,
field: auto,
err: ref CatchableError) =
err: ref CatchableError) {.gcsafe, raises: [WinregError].} =
var ex = new GenericWinregReaderError
ex.deserializedField = fieldName
ex.innerException = err
Expand All @@ -39,7 +41,8 @@ proc init*(T: type WinregReader,
result.hKey = hKey
result.path = path

proc readValue*[T](r: var WinregReader, value: var T) =
proc readValue*[T](r: var WinregReader, value: var T)
{.gcsafe, raises: [SerializationError, IOError].} =
mixin readValue
# TODO: reduce allocation

Expand Down Expand Up @@ -88,3 +91,5 @@ proc readValue*[T](r: var WinregReader, value: var T) =
else:
const typeName = typetraits.name(T)
{.fatal: "Failed to convert from Winreg an unsupported type: " & typeName.}

{.pop.}
4 changes: 4 additions & 0 deletions confutils/winreg/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,9 @@ const
HKCR* = HKEY_CLASSES_ROOT
HKU* = HKEY_USERS

{.push gcsafe, raises: [].}

proc `==`*(a, b: HKEY): bool {.borrow.}
proc `==`*(a, b: RegType): bool {.borrow.}

{.pop.}
14 changes: 12 additions & 2 deletions confutils/winreg/utils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const
RT_QWORD* = 0x00000040
RT_ANY* = 0x0000ffff

{.push gcsafe, raises: [].}

proc regGetValue(hKey: HKEY, lpSubKey, lpValue: cstring,
dwFlags: int32, pdwType: ptr RegType,
pvData: pointer, pcbData: ptr int32): int32 {.
Expand All @@ -39,12 +41,18 @@ template call(f) =
if f != 0:
return false

template safeCast(destType: type, src: typed): auto =
when sizeof(src) < sizeof(destType):
destType(src)
else:
cast[destType](src)

proc setValue*(hKey: HKEY, path, key: string, val: SomePrimitives): bool =
when sizeof(val) < 8:
var dw = cast[int32](val)
var dw = int32.safeCast(val)
call regSetValue(hKey, path, key, REG_DWORD, dw.addr, sizeof(dw).int32)
else:
var dw = cast[int64](val)
var dw = int64.safeCast(val)
call regSetValue(hKey, path, key, REG_QWORD, dw.addr, sizeof(dw).int32)
result = true

Expand Down Expand Up @@ -160,3 +168,5 @@ func constructPath*(root: string, keys: openArray[string]): string =
result.add keys[i]
if i < keys.len-2:
result. add '\\'

{.pop.}
4 changes: 4 additions & 0 deletions confutils/winreg/winreg_serialization.nim
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import
export
serialization, reader, writer, types

{.push gcsafe, raises: [].}

serializationFormat Winreg

Winreg.setReader WinregReader
Expand Down Expand Up @@ -65,3 +67,5 @@ template saveFile*(_: type Winreg, filename: string, value: auto, params: vararg
let (hKey, path) = parseWinregPath(filename)
var writer = unpackArgs(init, [WinregWriter, hKey, path, params])
writer.writeValue(value)

{.pop.}
4 changes: 4 additions & 0 deletions confutils/winreg/writer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ type
path: string
key: seq[string]

{.push gcsafe, raises: [].}

proc init*(T: type WinregWriter,
hKey: HKEY, path: string): T =
result.hKey = hKey
Expand Down Expand Up @@ -55,3 +57,5 @@ proc writeValue*(w: var WinregWriter, value: auto) {.raises: [IOError].} =
else:
const typeName = typetraits.name(value.type)
{.fatal: "Failed to convert to Winreg an unsupported type: " & typeName.}

{.pop.}
File renamed without changes.
3 changes: 0 additions & 3 deletions tests/test_config_file.nim
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ type
field_a: int
field_b: string

CheckPoint = int
RuntimePreset = int
GraffitiBytes = array[16, byte]
WalletName = string

VCStartUpCmd = enum
VCNoCommand
Expand Down

0 comments on commit 57ff0b8

Please sign in to comment.