summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorgemath <33119724+gemath@users.noreply.github.com>2018-06-19 17:13:33 +0000
committerDominik Picheta <dominikpicheta@googlemail.com>2018-06-19 18:13:33 +0100
commitdb68bbe4f7e3ed2c6321e46d9b4d4977f1855a4e (patch)
treed37c5a6f7cda8957dbf5e82974c7591a882f60a4
parent3ca11def6d320ff5120a3d438cffbdf25a72af7b (diff)
downloadNim-db68bbe4f7e3ed2c6321e46d9b4d4977f1855a4e.tar.gz
Pegs AST read access (#8050)
* Make PEG AST nodes readable from outside the module.

* Added a test module for the pegs stdlib module.

* Edited changelog.

* Renamed ``sons`` iterator to ``items``, added ``pairs``, inlined both.

* Updated entry and moved it to the right category.
-rw-r--r--changelog.md3
-rw-r--r--lib/pure/pegs.nim23
-rw-r--r--tests/stdlib/tpegs.nim78
3 files changed, 102 insertions, 2 deletions
diff --git a/changelog.md b/changelog.md
index 4067cb693..4a490dfdd 100644
--- a/changelog.md
+++ b/changelog.md
@@ -107,6 +107,9 @@
   use the Nim VM in a native Nim application.
 - Added the parameter ``val`` for the ``CritBitTree[T].incl`` proc.
 - The proc ``tgamma`` was renamed to ``gamma``. ``tgamma`` is deprecated.
+- The ``pegs`` module now exports getters for the fields of its ``Peg`` and ``NonTerminal``
+  object types. ``Peg``s with child nodes now have the standard ``items`` and ``pairs``
+  iterators.
 
 ### Language additions
 
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index 39c5790ed..d16527a56 100644
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -32,7 +32,7 @@ const
                        ## can be captured. More subpatterns cannot be captured!
 
 type
-  PegKind = enum
+  PegKind* = enum
     pkEmpty,
     pkAny,              ## any character (.)
     pkAnyRune,          ## any Unicode character (_)
@@ -67,7 +67,7 @@ type
     pkRule,             ## a <- b
     pkList,             ## a, b
     pkStartAnchor       ## ^      --> Internal DSL: startAnchor()
-  NonTerminalFlag = enum
+  NonTerminalFlag* = enum
     ntDeclared, ntUsed
   NonTerminalObj = object         ## represents a non terminal symbol
     name: string                  ## the name of the symbol
@@ -86,6 +86,25 @@ type
     else: sons: seq[Peg]
   NonTerminal* = ref NonTerminalObj
 
+proc name*(nt: NonTerminal): string = nt.name
+proc line*(nt: NonTerminal): int = nt.line
+proc col*(nt: NonTerminal): int = nt.col
+proc flags*(nt: NonTerminal): set[NonTerminalFlag] = nt.flags
+proc rule*(nt: NonTerminal): Peg = nt.rule
+
+proc kind*(p: Peg): PegKind = p.kind
+proc term*(p: Peg): string = p.term
+proc ch*(p: Peg): char = p.ch
+proc charChoice*(p: Peg): ref set[char] = p.charChoice
+proc nt*(p: Peg): NonTerminal = p.nt
+proc index*(p: Peg): range[0..MaxSubpatterns] = p.index
+iterator items*(p: Peg): Peg {.inline.} =
+  for s in p.sons:
+    yield s
+iterator pairs*(p: Peg): (int, Peg) {.inline.} =
+  for i in 0 ..< p.sons.len:
+    yield (i, p.sons[i])
+
 proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} =
   ## constructs a PEG from a terminal string
   if t.len != 1:
diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim
new file mode 100644
index 000000000..e5b709a66
--- /dev/null
+++ b/tests/stdlib/tpegs.nim
@@ -0,0 +1,78 @@
+discard """
+  output: '''
+pkNonTerminal: Sum @(2, 3)
+  pkSequence: (Product (('+' / '-') Product)*)
+    pkNonTerminal: Product @(3, 7)
+      pkSequence: (Value (('*' / '/') Value)*)
+        pkNonTerminal: Value @(4, 5)
+          pkOrderedChoice: (([0-9] [0-9]*) / ('(' Expr ')'))
+            pkSequence: ([0-9] [0-9]*)
+              pkCharChoice: [0-9]
+              pkGreedyRepSet: [0-9]*
+            pkSequence: ('(' Expr ')')
+              pkChar: '('
+              pkNonTerminal: Expr @(1, 4)
+                pkNonTerminal: Sum @(2, 3)
+              pkChar: ')'
+        pkGreedyRep: (('*' / '/') Value)*
+          pkSequence: (('*' / '/') Value)
+            pkOrderedChoice: ('*' / '/')
+              pkChar: '*'
+              pkChar: '/'
+            pkNonTerminal: Value @(4, 5)
+    pkGreedyRep: (('+' / '-') Product)*
+      pkSequence: (('+' / '-') Product)
+        pkOrderedChoice: ('+' / '-')
+          pkChar: '+'
+          pkChar: '-'
+        pkNonTerminal: Product @(3, 7)
+'''
+"""
+
+import strutils, streams
+import pegs
+
+const
+  indent = "  "
+
+let
+  pegSrc = """
+Expr <- Sum
+Sum <- Product (('+' / '-') Product)*
+Product <- Value (('*' / '/') Value)*
+Value <- [0-9]+ / '(' Expr ')'
+  """
+  pegAst: Peg = pegSrc.peg
+
+var
+  outp = newStringStream()
+  processed: seq[string] = @[]
+
+proc prt(outp: Stream, kind: PegKind, s: string; level: int = 0) =
+  outp.writeLine indent.repeat(level) & "$1: $2" % [$kind, s]
+
+proc recLoop(p: Peg, level: int = 0) =
+  case p.kind
+  of pkEmpty..pkWhitespace:
+    discard
+  of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle:
+    outp.prt(p.kind, $p, level)
+  of pkChar, pkGreedyRepChar:
+    outp.prt(p.kind, $p, level)
+  of pkCharChoice, pkGreedyRepSet:
+    outp.prt(p.kind, $p, level)
+  of pkNonTerminal:
+    outp.prt(p.kind,
+      "$1 @($3, $4)" % [p.nt.name, $p.nt.rule.kind, $p.nt.line, $p.nt.col], level)
+    if not(p.nt.name in processed):
+      processed.add p.nt.name
+      p.nt.rule.recLoop level+1
+  of pkBackRef..pkBackRefIgnoreStyle:
+    outp.prt(p.kind, $p, level)
+  else:
+    outp.prt(p.kind, $p, level)
+    for s in items(p):
+      s.recLoop level+1
+
+pegAst.recLoop
+echo outp.data
\ No newline at end of file