nip/src/nimpak/kdl_parser.nim

178 lines
4.8 KiB
Nim

# SPDX-License-Identifier: LSL-1.0
# Copyright (c) 2026 Markus Maiwald
# Stewardship: Self Sovereign Society Foundation
#
# This file is part of the Nexus Sovereign Core.
# See legal/LICENSE_SOVEREIGN.md for license terms.
## KDL Parser Integration for NIP
## Provides KDL parsing functionality for NIP configuration and package files
import std/[tables, options]
import kdl
# Re-export main types
export kdl.KdlVal, kdl.KdlNode, kdl.KdlDoc, kdl.KValKind, kdl.KdlError
# Helper procs for easier value access
proc getString*(val: KdlVal): string =
## Get string value, raises if not a string
if val.kind != KString:
raise newException(ValueError, "KdlVal is not a string")
val.str
proc getInt*(val: KdlVal): int64 =
## Get int value, raises if not an int
if val.kind != KInt:
raise newException(ValueError, "KdlVal is not an int")
val.num
proc getFloat*(val: KdlVal): float64 =
## Get float value, raises if not a float
if val.kind != KFloat:
raise newException(ValueError, "KdlVal is not a float")
val.fnum
proc getBool*(val: KdlVal): bool =
## Get bool value, raises if not a bool
if val.kind != KBool:
raise newException(ValueError, "KdlVal is not a bool")
val.boolean
proc isString*(val: KdlVal): bool =
## Check if value is a string
val.kind == KString
proc isInt*(val: KdlVal): bool =
## Check if value is an int
val.kind == KInt
proc isFloat*(val: KdlVal): bool =
## Check if value is a float
val.kind == KFloat
proc isBool*(val: KdlVal): bool =
## Check if value is a bool
val.kind == KBool
proc isNull*(val: KdlVal): bool =
## Check if value is null
val.kind == KNull
# Helper procs for node access
proc getArg*(node: KdlNode, idx: int): KdlVal =
## Get argument by index
if idx < 0 or idx >= node.args.len:
raise newException(IndexDefect, "Argument index out of bounds")
node.args[idx]
proc getArgString*(node: KdlNode, idx: int): string =
## Get string argument by index
node.getArg(idx).getString()
proc getArgInt*(node: KdlNode, idx: int): int64 =
## Get int argument by index
node.getArg(idx).getInt()
proc getArgBool*(node: KdlNode, idx: int): bool =
## Get bool argument by index
node.getArg(idx).getBool()
proc getProp*(node: KdlNode, key: string): KdlVal =
## Get property by key
if not node.props.hasKey(key):
raise newException(KeyError, "Property not found: " & key)
node.props[key]
proc getPropString*(node: KdlNode, key: string): string =
## Get string property by key
node.getProp(key).getString()
proc getPropInt*(node: KdlNode, key: string): int64 =
## Get int property by key
node.getProp(key).getInt()
proc getPropBool*(node: KdlNode, key: string): bool =
## Get bool property by key
node.getProp(key).getBool()
proc getPropString*(node: KdlNode, key: string, default: string): string =
## Get string property by key with default
if node.props.hasKey(key):
node.props[key].getString()
else:
default
proc getPropInt*(node: KdlNode, key: string, default: int64): int64 =
## Get int property by key with default
if node.props.hasKey(key):
node.props[key].getInt()
else:
default
proc getPropBool*(node: KdlNode, key: string, default: bool): bool =
## Get bool property by key with default
if node.props.hasKey(key):
node.props[key].getBool()
else:
default
proc hasProp*(node: KdlNode, key: string): bool =
## Check if node has a property
node.props.hasKey(key)
proc findChild*(node: KdlNode, name: string): Option[KdlNode] =
## Find first child node by name
for child in node.children:
if child.name == name:
return some(child)
none(KdlNode)
proc findChildren*(node: KdlNode, name: string): seq[KdlNode] =
## Find all child nodes by name
result = @[]
for child in node.children:
if child.name == name:
result.add(child)
# Document helpers
proc findNode*(doc: KdlDoc, name: string): Option[KdlNode] =
## Find first top-level node by name
for node in doc:
if node.name == name:
return some(node)
none(KdlNode)
proc findNodes*(doc: KdlDoc, name: string): seq[KdlNode] =
## Find all top-level nodes by name
result = @[]
for node in doc:
if node.name == name:
result.add(node)
# Parsing functions
proc parseKdlString*(content: string): KdlDoc =
## Parse KDL from string
try:
result = kdl.parseKdl(content)
except KdlError as e:
raise newException(ValueError, "KDL parse error: " & e.msg)
proc parseKdlFile*(path: string): KdlDoc =
## Parse KDL from file
try:
result = kdl.parseKdlFile(path)
except KdlError as e:
raise newException(ValueError, "KDL parse error in file " & path & ": " & e.msg)
except IOError as e:
raise newException(IOError, "Failed to read file " & path & ": " & e.msg)
# Serialization
proc toKdlString*(doc: KdlDoc): string =
## Convert KdlDoc to string
$doc
proc toKdlString*(node: KdlNode): string =
## Convert KdlNode to string
$node