Skip to content

Commit df0c275

Browse files
committed
wip yaml parser
Signed-off-by: George Lemon <georgelemon@protonmail.com>
1 parent 9831909 commit df0c275

1 file changed

Lines changed: 136 additions & 17 deletions

File tree

src/openparser/yaml.nim

Lines changed: 136 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ type
325325
yamlArray
326326
yamlNull
327327

328-
YamlNode* {.acyclic.} = object
328+
YamlNode* {.acyclic.} = ref object
329329
case kind*: YamlValueKind
330330
of yamlInteger:
331331
intValue*: int64
@@ -344,6 +344,108 @@ type
344344
YAMLObject* = OrderedTableRef[string, YamlNode]
345345
## Represents a simple
346346

347+
proc newYamlString*(s: string): YamlNode =
348+
## Create a new YamlNode of kind yamlString
349+
YamlNode(kind: yamlString, strValue: s)
350+
351+
proc newYamlFloat*(f: float64): YamlNode =
352+
## Create a new YamlNode of kind yamlFloat
353+
YamlNode(kind: yamlFloat, floatValue: f)
354+
355+
proc newYamlInteger*(i: int64): YamlNode =
356+
## Create a new YamlNode of kind yamlInteger
357+
YamlNode(kind: yamlInteger, intValue: i)
358+
359+
proc newYamlBoolean*(b: bool): YamlNode =
360+
## Create a new YamlNode of kind yamlBoolean
361+
YamlNode(kind: yamlBoolean, boolValue: b)
362+
363+
proc newYamlNull*(): YamlNode =
364+
## Create a new YamlNode of kind yamlNull
365+
YamlNode(kind: yamlNull)
366+
367+
proc newYamlObject*(): YamlNode =
368+
## Create a new YamlNode of kind yamlObject
369+
YamlNode(kind: yamlObject, objValue: newOrderedTable[string, YamlNode]())
370+
371+
proc newYamlArray*(): YamlNode =
372+
## Create a new YamlNode of kind yamlArray
373+
YamlNode(kind: yamlArray, arrValue: @[])
374+
375+
proc get*(n: YamlNode, key: string): YamlNode =
376+
## Recursively access nested YAML data using dot-separated keys.
377+
## Example: get(config, "user.name")
378+
if n == nil or key.len == 0:
379+
return nil
380+
if '.' notin key:
381+
if n.kind == yamlObject and n.objValue.hasKey(key):
382+
return n.objValue[key]
383+
else:
384+
return nil
385+
let dotIdx = key.find('.')
386+
let head = key[0 ..< dotIdx]
387+
let tail = key[dotIdx+1 .. ^1]
388+
let nextNode =
389+
if n.kind == yamlObject and n.objValue.hasKey(head):
390+
n.objValue[head]
391+
else:
392+
nil
393+
if nextNode == nil:
394+
return nil
395+
return get(nextNode, tail)
396+
397+
proc get*(obj: YamlObject, key: string): YamlNode =
398+
## Access a value from a YAMLObject using a key
399+
if obj.hasKey(key):
400+
return obj[key]
401+
else:
402+
return nil
403+
404+
proc put*(obj: YamlObject, key: string, value: YamlNode) =
405+
## Insert or update a key-value pair in a YAMLObject
406+
obj[key] = value
407+
408+
proc getStr*(n: YamlNode): string =
409+
## Get string value or "" if not a string node
410+
if n != nil and n.kind == yamlString:
411+
result = n.strValue
412+
413+
proc getInt*(n: YamlNode): int64 =
414+
## Get integer value or 0 if not an integer node
415+
if n != nil and n.kind == yamlInteger:
416+
result = n.intValue
417+
418+
proc getFloat*(n: YamlNode): float64 =
419+
## Get float value or 0.0 if not a float node
420+
if n != nil and n.kind == yamlFloat:
421+
result = n.floatValue
422+
423+
proc getBool*(n: YamlNode): bool =
424+
## Get boolean value or false if not a boolean node
425+
if n != nil and n.kind == yamlBoolean:
426+
result = n.boolValue
427+
428+
proc getArray*(n: YamlNode): seq[YamlNode] =
429+
## Get array value or empty seq if not an array node
430+
if n != nil and n.kind == yamlArray:
431+
result = n.arrValue
432+
433+
proc getObject*(n: YamlNode): OrderedTableRef[string, YamlNode] =
434+
## Get object value or empty table if not an object node
435+
if n != nil and n.kind == yamlObject:
436+
result = n.objValue
437+
438+
proc getValue*(v: YamlNode): string =
439+
## Get the string representation of a YamlNode value (for debugging)
440+
case v.kind
441+
of yamlNull: "null"
442+
of yamlBoolean: $v.boolValue
443+
of yamlInteger: $v.intValue
444+
of yamlFloat: $v.floatValue
445+
of yamlString: v.strValue
446+
of yamlObject: "{...}"
447+
of yamlArray: "[...]"
448+
347449
proc advance(p: var YamlParser) {.inline.} =
348450
p.prev = p.curr
349451
p.curr = p.next
@@ -389,21 +491,35 @@ proc parseSequence(p: var YamlParser, indent: int): seq[YamlNode]
389491
proc parseInlineArray(p: var YamlParser): YamlNode
390492
proc parseInlineObject(p: var YamlParser): YamlNode
391493

392-
proc parsePlainUnquoted(p: var YamlParser): YamlNode =
393-
## Parse plain scalar on the same line (e.g.: title: hello world)
494+
proc parsePlainUnquoted(p: var YamlParser, inlineMode = false): YamlNode =
495+
## Parse plain scalar on the same line.
496+
## In inline mode, stop at ',', ']' and '}'.
394497
let lineNo = p.curr.line
395-
var parts: seq[string] = @[]
396-
var firstTok = p.curr
498+
let firstTok = p.curr
499+
var count = 0
500+
var buf = ""
397501

398-
while p.curr.kind in {ytkIdentifier, ytkInteger, ytkFloat} and p.curr.line == lineNo:
399-
parts.add(p.curr.value)
502+
while p.curr.kind != ytkEOF and p.curr.line == lineNo:
503+
if p.curr.kind == ytkComment:
504+
break
505+
if inlineMode and p.curr.kind in {ytkComma, ytkRB, ytkRC}:
506+
break
507+
508+
if count > 0 and p.curr.wsno > 0:
509+
buf.add(repeat(' ', p.curr.wsno))
510+
511+
# prefer token value; fallback to token text
512+
# for punctuation-like tokens
513+
let part = if p.curr.value.len > 0: p.curr.value else: tokenText(p.curr)
514+
buf.add(part)
515+
516+
inc count
400517
advance(p)
401518

402-
if parts.len == 1:
403-
# preserve bool/null/integer/float behavior
404-
result = getScalarValue(firstTok)
519+
if count == 1:
520+
result = getScalarValue(firstTok) # preserves bool/int/float/null coercion
405521
else:
406-
result = YamlNode(kind: yamlString, strValue: parts.join(" "))
522+
result = YamlNode(kind: yamlString, strValue: buf)
407523

408524
proc parseBlockString(p: var YamlParser, parentIndent: int, folded: bool): YamlNode =
409525
## Parse YAML block scalar after '|' or '>'
@@ -529,29 +645,32 @@ proc parseMapping(p: var YamlParser, indent: int): YAMLObject =
529645
result[key] = YamlNode(kind: yamlNull)
530646

531647
proc parseValue(p: var YamlParser, parentIndent: int): YamlNode =
648+
let inlineMode = parentIndent < 0
532649
case p.curr.kind
533650
of ytkIdentifier:
534651
if p.next.kind == ytkColon and p.curr.indent > parentIndent:
535652
let obj = parseMapping(p, p.curr.indent)
536653
result = YamlNode(kind: yamlObject, objValue: obj)
537654
else:
538-
result = parsePlainUnquoted(p)
655+
result = parsePlainUnquoted(p, inlineMode)
539656
of ytkString, ytkFloat, ytkInteger:
540-
result = parseScalar(p)
657+
result = parsePlainUnquoted(p, inlineMode)
541658
of ytkLB:
542659
result = parseInlineArray(p)
543660
of ytkLC:
544661
result = parseInlineObject(p)
545662
of ytkDash:
546-
let arr = parseSequence(p, p.curr.indent) # use indentation, not wsno
663+
let arr = parseSequence(p, p.curr.indent)
547664
result = YamlNode(kind: yamlArray, arrValue: arr)
548665
of ytkPipe:
549666
result = parseBlockString(p, parentIndent, folded = false)
550667
of ytkGT:
551668
result = parseBlockString(p, parentIndent, folded = true)
552669
else:
553-
raise newException(ValueError,
554-
"Unexpected value token " & $p.curr.kind & " at line " & $p.curr.line & ", col " & $p.curr.col)
670+
raise newException(
671+
ValueError,
672+
"Unexpected value token " & $p.curr.kind & " at line " & $p.curr.line & ", col " & $p.curr.col
673+
)
555674

556675
proc parseRoot(p: var YamlParser): YAMLObject =
557676
result = parseMapping(p, 0)
@@ -576,7 +695,7 @@ proc nimStringLiteral(s: string): string =
576695
#
577696
# Dump hook to for converting YAMLObject to JSON
578697
#
579-
proc dumpHook(s: var string, v: YamlNode) =
698+
proc dumpHook*(s: var string, v: YamlNode) =
580699
case v.kind
581700
of yamlNull:
582701
s.add("null")

0 commit comments

Comments
 (0)