mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-09 14:32:53 +00:00
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.
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
78
tests/stdlib/tpegs.nim
Normal file
78
tests/stdlib/tpegs.nim
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user