Urbit / Docs

4f: parsing (rule builders)

++bass

Parser modifier: LSB ordered ++list as atom of a ++base.

Accepts

wuc is an atom.

tyd is a ++rule.

Produces

A ++rule.

Source

    ++  bass
      |*  {wuc/@ tyd/rule}
      %+  cook
        |=  waq/(list @)
        %+  roll
          waq
        =|({p/@ q/@} |.((add p (mul wuc q))))
      tyd
    ::

Examples

    > (scan "123" (bass 10 (star dit)))
    q=123
    > (scan "123" (bass 8 (star dit)))
    q=83
    > `@ub`(scan "123" (bass 8 (star dit)))
    0b101.0011

++boss

Parser modifier: LSB

Ordered ++list as atom of a ++base.

Accepts

wuc is an atom.

tyd is a ++rule.

Produces

A ++rule.

Source

    ++  boss
      |*  {wuc/@ tyd/rule}
      %+  cook
        |=  waq/(list @)
        %+  reel
          waq
        =|({p/@ q/@} |.((add p (mul wuc q))))
      tyd
    ::

Examples

    > (scan "123" (boss 10 (star dit)))
    q=321

    > `@t`(scan "bam" (boss 256 (star alp)))
    'bam'

    > `@ux`(scan "bam" (boss 256 (star alp)))
    0x6d.6162

++cold

Replace with constant

Parser modifier. Accepts a ++rule sef and produces a parser that produces a constant cus, assuming sef is successful.

Accepts

cus is a constant noun.

sef is a ++rule.

Produces

An ++edge.

Source

    ++  cold                                                ::  replace w+ constant
      ~/  %cold
      |*  {cus/* sef/rule}
      ~/  %fun
      |=  tub/nail
      =+  vex=(sef tub)
      ?~  q.vex
        vex
      [p=p.vex q=[~ u=[p=cus q=q.u.q.vex]]]

Examples

        > ((cold %foo (just 'a')) [[1 1] "abc"])
        [p=[p=1 q=2] q=[~ u=[p=%foo q=[p=[p=1 q=2] q="bc"]]]]

        > ((cold %foo (just 'a')) [[1 1] "bc"])
        [p=[p=1 q=1] q=~]

++cook

Apply gate

Parser modifier. Produces a parser that takes a (successful) result of a ++rule sef and slams it through poq.

Accepts

poq is a gate.

sef is a ++rule.

Produces

An ++rule.

Source

    ++  cook                                                ::  apply gate
      ~/  %cook
      |*  {poq/gate sef/rule}
      ~/  %fun
      |=  tub/nail
      =+  vex=(sef tub)
      ?~  q.vex
        vex
      [p=p.vex q=[~ u=[p=(poq p.u.q.vex) q=q.u.q.vex]]]
    ::

Examples

        > ((cook ,@ud (just 'a')) [[1 1] "abc"])
        [p=[p=1 q=2] q=[~ u=[p=97 q=[p=[p=1 q=2] q="bc"]]]]

        > ((cook ,@tas (just 'a')) [[1 1] "abc"])
        [p=[p=1 q=2] q=[~ u=[p=%a q=[p=[p=1 q=2] q="bc"]]]]

        > ((cook |=([email protected] +(a)) (just 'a')) [[1 1] "abc"])
        [p=[p=1 q=2] q=[~ u=[p=98 q=[p=[p=1 q=2] q="bc"]]]]

        > ((cook |=([email protected] `@t`+(a)) (just 'a')) [[1 1] "abc"])
        [p=[p=1 q=2] q=[~ u=[p='b' q=[p=[p=1 q=2] q="bc"]]]]

++easy

Always parse

Parser generator. Produces a parser that succeeds with given noun huf without consuming any text.

Accepts

huf is a noun.

Produces

A ++rule.

Source

    ++  easy                                                ::  always parse
      ~/  %easy
      |*  huf=*
      ~/  %fun
      |=  tub=nail
      ^-  (like ,_huf)
      [p=p.tub q=[~ u=[p=huf q=tub]]]
    ::

Examples

    > ((easy %foo) [[1 1] "abc"])
    [p=[p=1 q=1] q=[~ [p=%foo q=[p=[p=1 q=1] q="abc"]]]]

    > ((easy %foo) [[1 1] "bc"])
    [p=[p=1 q=1] q=[~ [p=%foo q=[p=[p=1 q=1] q="bc"]]]]

    > ((easy 'a') [[1 1] "bc"])
    [p=[p=1 q=1] q=[~ [p='a' q=[p=[p=1 q=1] q="bc"]]]]

++full

Parse to end

Parser modifier. Accepts a ++rule sef, and produces a parser that succeeds only when the of tub is fully consumed using sef.

Accepts

sef is a ++rule.

Produces

A ++rule.

Source

    ++  full                                                :: parse to end
      |*  sef=_rule
      |=  tub=nail
      =+  vex=(sef tub)
      ?~(q.vex vex ?:(=(~ q.q.u.q.vex) vex [p=p.vex q=~]))
    ::

Examples

    > ((full (just 'a')) [[1 1] "ab"])
    [p=[p=1 q=2] q=~]

    > ((full (jest 'ab')) [[1 1] "ab"])
    [p=[p=1 q=3] q=[~ u=[p='ab' q=[p=[p=1 q=3] q=""]]]]

    > ((full ;~(plug (just 'a') (just 'b'))) [[1 1] "ab"])
    [p=[p=1 q=3] q=[~ u=[p=[~~a ~~b] q=[p=[p=1 q=3] q=""]]]]

++funk

Add to tape

Parser modifier: prepend text to ++tape before applying parser.

Accepts

pre is a ++tape

sef is a ++rule

Produces

A ++rule.

Source

    ++  funk                                                ::  add to tape first
      |*  {pre/tape sef/rule}
      |=  tub/nail
      (sef p.tub (weld pre q.tub))

Examples

    > ((funk "abc prefix-" (jest 'abc')) [[1 1] "to be parsed"])
    [p=[p=1 q=4] q=[~ [p='abc' q=[p=[p=1 q=4] q=" prefix-to be parsed"]]]]

    > ((funk "parse" (just 'a')) [[1 4] " me"])
    [p=[p=1 q=4] q=~]

++here

Place-based apply

Parser modifier. Similar to ++cook in that it produces a parser that takes a (successful) result of sef and slams it through hez. hez accepts a ++pint a and a noun b, which is what the parser parsed.

Accepts

hez is a gate.

sef is a ++rule

Produces

A ++rule.

Source

    ++  here                                                ::  place-based apply
      ~/  %here
      |*  {hez/_|=({a/pint b/*} [a b]) sef/rule}
      ~/  %fun
      |=  tub/nail
      =+  vex=(sef tub)
      ?~  q.vex
        vex
      [p=p.vex q=[~ u=[p=(hez [p.tub p.q.u.q.vex] p.u.q.vex) q=q.u.q.vex]]]
    ::

Examples

    > (scan "abc" (star alf))
    "abc"

    > (scan "abc" (here |*(^ +<) (star alf)))
    [[[p=1 q=1] p=1 q=4] "abc"]

    > (scan "abc" (star (here |*(^ +<) alf)))
    ~[[[[p=1 q=1] p=1 q=2] ~~a] [[[p=1 q=2] p=1 q=3] ~~b] [[[p=1 q=3] p=1 q=4] ~~c]]

++ifix

Infix

Parser modifier: surround with pair of ++rules, the output of which is discarded.

Accepts

fel is a pair of ++rules.

hof is a ++rule.

Produces

A ++rule.

Source

    ++  ifix
      |*  {fel/{rule rule} hof/rule}
      ~!  +<
      ~!  +<:-.fel
      ~!  +<:+.fel
      ;~(pfix -.fel ;~(sfix hof +.fel))
    ::

Examples

    > (scan "-40-" (ifix [hep hep] dem))
    q=40

    > (scan "4my4" (ifix [dit dit] (star alf)))
    "my"

++jest

Match a cord

Match and consume a cord.

Accepts

daf is a @t.

Produces

An ++edge.

Source

    ++  jest                                                ::  match a cord
      |=  daf/@t
      |=  tub/nail
      =+  fad=daf
      |-  ^-  (like @t)
      ?:  =(`@`0 daf)
        [p=p.tub q=[~ u=[p=fad q=tub]]]
      ?:  |(?=($~ q.tub) !=((end 3 1 daf) i.q.tub))
        (fail tub)
      $(p.tub (lust i.q.tub p.tub), q.tub t.q.tub, daf (rsh 3 1 daf))
    ::

Examples

    > ((jest 'abc') [[1 1] "abc"])
    [p=[p=1 q=4] q=[~ [p='abc' q=[p=[p=1 q=4] q=""]]]]

    > (scan "abc" (jest 'abc'))
    'abc'

    > (scan "abc" (jest 'acb'))
    ! {1 2}
    ! 'syntax-error'
    ! exit

    > ((jest 'john doe') [[1 1] "john smith"])
    [p=[p=1 q=6] q=~]

    > ((jest 'john doe') [[1 1] "john doe"])
    [p=[p=1 q=9] q=[~ [p='john doe' q=[p=[p=1 q=9] q=""]]]]

++just

Match a char

Match and consume a single character.

Accepts

daf is a ++char

Produces

A ++rule.

Source

    ++  just                                                ::  XX redundant, jest
      ~/  %just                                             ::  match a char
      |=  daf/char
      ~/  %fun
      |=  tub/nail
      ^-  (like char)
      ?~  q.tub
        (fail tub)
      ?.  =(daf i.q.tub)
        (fail tub)
      (next tub)
    ::

Examples

    > ((just 'a') [[1 1] "abc"])
    [p=[p=1 q=2] q=[~ [p=~~a q=[p=[p=1 q=2] q="bc"]]]]

    > (scan "abc" (just 'a'))
    ! {1 2}
    ! 'syntax-error'
    ! exit

    > (scan "a" (just 'a'))

    ~~a
    > (scan "%" (just '%'))
    ~~~25.

++knee

Recursive parsers

Used for recursive parsers, which would otherwise be infinite when compiled.

Accepts

gar is a noun.

sef is a gate that accepts a ++rule

Produces

A ++rule.

Source

    ++  knee                                                ::  callbacks
      |*  {gar/* sef/_|.(*rule)}
      |=  tub/nail
      ^-  (like _gar)
      ((sef) tub)
    ::

Examples

    > |-(;~(plug prn ;~(pose $ (easy ~))))
    ! rest-loop
    ! exit

    > |-(;~(plug prn ;~(pose (knee *tape |.(^$)) (easy ~))))
    < 1.obo
      [ c=c=tub=[p=[[email protected] [email protected]] q=""]
          b
        < 1.bes
          [ c=tub=[p=[[email protected] [email protected]] q=""]
            b=<1.tnv [tub=[p=[[email protected] [email protected]] q=""] <1.ktu [[email protected] <414.fvk 101.jzo 1.ypj %164>]>]>
            a=<1.fvg [tub=[p=[[email protected] [email protected]] q=""] <1.khu [[[email protected] [email protected]] <414.fvk 101.jzo 1.ypj %164>]>]>
            v=<414.fvk 101.jzo 1.ypj %164>
          ]
        >
          a
        ... 450 lines omitted ...
      ]
    >
    > (scan "abcd" |-(;~(plug prn ;~(pose (knee *tape |.(^$)) (easy ~)))))
    [~~a "bcd"]

++mask

Match char

Parser generator. Matches the next character if it is in a list of characters.

Accepts

bud is a list of ++char

Produces

A ++rule.

Source

    ++  mask                                                ::  match char in set
      ~/  %mask
      |=  bud/(list char)
      ~/  %fun
      |=  tub/nail
      ^-  (like char)
      ?~  q.tub
        (fail tub)
      ?.  (lien bud |=(a/char =(i.q.tub a)))
        (fail tub)
      (next tub)
    ::

Examples

    > (scan "a" (mask "cba"))
    ~~a

    > ((mask "abc") [[1 1] "abc"])
    [p=[p=1 q=2] q=[~ [p=~~a q=[p=[p=1 q=2] q="bc"]]]]

    > ((mask "abc") [[1 1] "bbc"])
    [p=[p=1 q=2] q=[~ [p=~~b q=[p=[p=1 q=2] q="bc"]]]]

    > ((mask "abc") [[1 1] "dbc"])
    [p=[p=1 q=1] q=~]

++more

Parse list with delimiter

Parser modifier: Parse a list of matches using a delimiter ++rule.

Accepts

bus is a ++rule.

fel is a ++rule.

Produces

A ++rule.

Source

    ++  more
      |*  {bus/rule fel/rule}
      ;~(pose (most bus fel) (easy ~))
    ::

Examples

    > (scan "" (more ace dem))
    ~

    > (scan "40 20" (more ace dem))
    [q=40 ~[q=20]]

    > (scan "40 20 60 1 5" (more ace dem))
    [q=40 ~[q=20 q=60 q=1 q=5]]

++most

Parse list of at least one match

Parser modifier: parse a ++list of at least one match using a delimiter ++rule.

Accepts

bus is a ++rule.

fel is a ++rule.

Produces

A ++rule.

Source

    ++  most
      |*  {bus/rule fel/rule}
      ;~(plug fel (star ;~(pfix bus fel)))
    ::

Examples

    > (scan "40 20" (most ace dem))
    [q=40 ~[q=20]]

    > (scan "40 20 60 1 5" (most ace dem))
    [q=40 ~[q=20 q=60 q=1 q=5]]

    > (scan "" (most ace dem))
    ! {1 1}
    ! exit

++next

Consume char

Consume any character, producing it as a result.

Accepts

tub is a ++nail

Produces

An ++edge.

Source

    ++  next                                                ::  consume a char
      |=  tub/nail
      ^-  (like char)
      ?~  q.tub
        (fail tub)
      =+  zac=(lust i.q.tub p.tub)
      [zac [~ i.q.tub [zac t.q.tub]]]
    ::

Examples

    > (next [[1 1] "ebc"])
    [p=[p=1 q=2] q=[~ [p=~~e q=[p=[p=1 q=2] q="bc"]]]]

    > (next [[1 1] "john jumps jones"])
    [p=[p=1 q=2] q=[~ [p=~~j q=[p=[p=1 q=2] q="ohn jumps jones"]]]]

++plus

List of at least one match.

Parser modifier: parse ++list of at least one match.

Accepts

fel is a ++rule.

Produces

A ++rule.

Source

    ++  plus  |*(fel/rule ;~(plug fel (star fel)))

Examples

    > (scan ">>>>" (cook lent (plus gar)))
    4

    > (scan "-  - " (plus ;~(pose ace hep)))
    [~~- "  - "]

    > `tape`(scan "-  - " (plus ;~(pose ace hep)))
    "-  - "

    > `(pole ,@t)`(scan "-  - " (plus ;~(pose ace hep)))
    ['-' [' ' [' ' ['-' [' ' ~]]]]]

++sear

Conditional ++cook

Conditional ++cook. Slams the result through a gate that produces a unit; if that unit is empty, fail.

Accepts

tub is a ++nail.

Produces

A ++rule.

Source

    ++  sear                                                ::  conditional cook
      |*  {pyq/$-(* (unit)) sef/rule}
      |=  tub/nail
      =+  vex=(sef tub)
      ?~  q.vex
        vex
      =+  gey=(pyq p.u.q.vex)
      ?~  gey
        [p=p.vex q=~]
      [p=p.vex q=[~ u=[p=u.gey q=q.u.q.vex]]]
    ::

Examples

    > ((sear |=(a/* [email protected](a (some a) ~)) (just `a`)) [[1 1] "abc"])
    [p=[p=1 q=2] q=[~ u=[p=97 q=[p=[p=1 q=2] q="bc"]]]]

    > ((sear |=(* ~) (just 'a')) [[1 1] "abc"])
    [p=[p=1 q=2] q=~]

++shim

Char in range

Match characters (++char) within a range.

Accepts

les is an atom.

mos is an atom.

Produces

A ++rule.

Source

    ++  shim                                                ::  match char in range
      ~/  %shim
      |=  {les/@ mos/@}
      ~/  %fun
      |=  tub/nail
      ^-  (like char)
      ?~  q.tub
        (fail tub)
      ?.  ?&((gte i.q.tub les) (lte i.q.tub mos))
        (fail tub)
      (next tub)
    ::

Examples

    > ((shim 'a' 'z') [[1 1] "abc"])
    [p=[p=1 q=2] q=[~ [p=~~a q=[p=[p=1 q=2] q="bc"]]]]

    > ((shim 'a' 'Z') [[1 1] "abc"])
    [p=[p=1 q=1] q=~]

    > ((shim 'a' 'Z') [[1 1] "Abc"])
    [p=[p=1 q=2] q=[~ [p=~~~41. q=[p=[p=1 q=2] q="bc"]]]]

++slug

Use gate to parse delimited list

Parser modifier: By composing with a gate, parse a delimited ++list of matches.

Accepts

bus is a ++rule.

fel is a ++rule.

Produces

A ++rule.

Source

    ++  slug
      |*  raq/_|*({a/* b/*} [a b])
      |*  {bus/rule fel/rule}
      ;~((comp raq) fel (stir +<+.raq raq ;~(pfix bus fel)))
    ::

Examples

    > (scan "20+5+110" ((slug add) lus dem))
    135

    > `@t`(scan "a b c" ((slug |=(a/[@ @t] (cat 3 a))) ace alp))
    'abc'

++stag

Add label

Add a label to an edge parsed by a rule.

Accepts

gob is a noun.

sef is a rule.

Produces

A ++rule.

Source

    ++  stag                                                ::  add a label
      ~/  %stag
      |*  {gob/* sef/rule}
      ~/  %fun
      |=  tub/nail
      =+  vex=(sef tub)
      ?~  q.vex
        vex
      [p=p.vex q=[~ u=[p=[gob p.u.q.vex] q=q.u.q.vex]]]
    ::

Examples

    > ((stag %foo (just 'a')) [[1 1] "abc"])
    [p=[p=1 q=2] q=[~ u=[p=[%foo ~~a] q=[p=[p=1 q=2] q="bc"]]]]

    > ((stag "xyz" (jest 'abc')) [[1 1] "abc"])
    [p=[p=1 q=4] q=[~ u=[p=["xyz" 'abc'] q=[p=[p=1 q=4] q=""]]]]

    > ((stag 10.000 (shim 0 100)) [[1 1] "abc"])
    [p=[p=1 q=2] q=[~ u=[p=[10.000 ~~a] q=[p=[p=1 q=2] q="bc"]]]]

++star

List of matches

Parser modifier: parse ++list of matches.

Accepts

fel is a ++rule.

Produces

    ++  star                                                ::  0 or more times
      |*  fel/rule
      (stir `(list _(wonk *fel))`~ |*({a/* b/*} [a b]) fel)

Examples

        > (scan "aaaaa" (just 'a'))
        ! {1 2}
        ! 'syntax-error'
        ! exit

        > (scan "aaaaa" (star (just 'a')))
        "aaaaa"

        > (scan "abcdef" (star (just 'a')))
        ! {1 2}
        ! 'syntax-error'
        ! exit

        > (scan "abcabc" (star (jest 'abc')))
        <|abc abc|>

        > (scan "john smith" (star (shim 0 200)))
        "john smith"

++stet

Add faces

Add faces [p q] to range-parser pairs in a list.

Accepts

leh is a list of range-parsers.

Produces

Source

    ++  stet
      |*  leh/(list {?(@ {@ @}) rule})
      |-
      ?~  leh
        ~
      [i=[p=-.i.leh q=+.i.leh] t=$(leh t.leh)]
    ::

Examples

    > (stet (limo [[5 (just 'a')] [1 (jest 'abc')] [[1 1] (shim 0 200)]
    [[1 10] (cold %foo (just 'a'))]~]))
    ~[
      [p=5 q=<1.lrk [tub=[p=[[email protected] [email protected]] q=""] <1.nqy [[email protected] <394.imz 97.kdz 1.xlc %164>]>]>]
      [p=1 q=<1.lrk [tub=[p=[[email protected] [email protected]] q=""] <1.nqy [[email protected] <394.imz 97.kdz 1.xlc %164>]>]>]
      [p=[1 1] q=<1.lrk [tub=[p=[[email protected] [email protected]] q=""] <1.nqy [[email protected] <394.imz 97.kdz 1.xlc %164>]>]>]
      [p=[1 10] q=<1.lrk [tub=[p=[[email protected] [email protected]] q=""] <1.nqy [[email protected] <394.imz 97.kdz 1.xlc %164>]>]>]
    ]
    > [[[1 1] (just 'a')] [[2 1] (shim 0 200)] ~]
    [ [[1 1] <1.tnv [tub=[p=[[email protected] [email protected]] q=""] <1.ktu [[email protected] <414.fvk 101.jzo 1.ypj %164>]>]>]
      [[2 1] <1.fvg [tub=[p=[[email protected] [email protected]] q=""] <1.khu [[[email protected] [email protected]] <414.fvk 101.jzo 1.ypj %164>]>]>]
      ~
    ]
    > (stet (limo [[[1 1] (just 'a')] [[2 1] (shim 0 200)] ~]))
    ~[
      [p=[1 1] q=<1.lrk [tub=[p=[[email protected] [email protected]] q=""] <1.nqy [[email protected] <394.imz 97.kdz 1.xlc %164>]>]>]
      [p=[2 1] q=<1.lrk [tub=[p=[[email protected] [email protected]] q=""] <1.nqy [[email protected] <394.imz 97.kdz 1.xlc %164>]>]>]
    ]

++stew

Switch by first

    ++  stew                                                ::  switch by first char
      ~/  %stew
      |*  leh/(list {p/?(@ {@ @}) q/rule})                  ::  char+range keys
      =+  ^=  wor                                           ::  range complete lth
          |=  {ort/?(@ {@ @}) wan/?(@ {@ @})}
          [email protected]  ort
            [email protected](wan (lth ort wan) (lth ort -.wan))
          [email protected](wan (lth +.ort wan) (lth +.ort -.wan))
      =+  ^=  hel                                           ::  build parser map
          =+  hel=`(tree _?>(?=(^ leh) i.leh))`~
          |-  ^+  hel
          ?~  leh
            ~
          =+  yal=$(leh t.leh)
          |-  ^+  hel
          ?~  yal
            [i.leh ~ ~]
          ?:  (wor p.i.leh p.n.yal)
            =+  nuc=$(yal l.yal)
            ?>  ?=(^ nuc)
            ?:  (vor p.n.yal p.n.nuc)
              [n.yal nuc r.yal]
            [n.nuc l.nuc [n.yal r.nuc r.yal]]
          =+  nuc=$(yal r.yal)
          ?>  ?=(^ nuc)
          ?:  (vor p.n.yal p.n.nuc)
            [n.yal l.yal nuc]
          [n.nuc [n.yal l.yal l.nuc] r.nuc]
      ~%  %fun  ..^$  ~
      |=  tub/nail
      ?~  q.tub
        (fail tub)
      |-
      ?~  hel
        (fail tub)
      ?:  [email protected]  p.n.hel
            =(p.n.hel i.q.tub)
          ?&((gte i.q.tub -.p.n.hel) (lte i.q.tub +.p.n.hel))
        ::  (q.n.hel [(lust i.q.tub p.tub) t.q.tub])
        (q.n.hel tub)
      ?:  (wor i.q.tub p.n.hel)
        $(hel l.hel)
      $(hel r.hel)
    ::

Parser generator. From an associative ++list of characters or character ranges to ++rules, construct a ++map, and parse ++tapes only with ++rules associated with a range that the ++tape's first character falls in.


++stir

Parse repeatedly

Parse with ++rule as many times as possible, and fold over results with a binary gate.

Accepts

rud is a noun.

raq is a gate that takes two nouns and produces a cell.

fel is a rule.

Produces

A rule.

Source

    ++  stun                                                ::  parse several times
      |*  {lig/{@ @} fel/rule}
      |=  tub/nail
      ^-  (like (list _(wonk (fel))))
      ?:  =(0 +.lig)
        [p.tub [~ ~ tub]]
      =+  vex=(fel tub)
      ?~  q.vex
        ?:  =(0 -.lig)
          [p.vex [~ ~ tub]]
        vex
      =+  ^=  wag  %=  $
                     -.lig  ?:(=(0 -.lig) 0 (dec -.lig))
                     +.lig  ?:(=(0 +.lig) 0 (dec +.lig))
                     tub  q.u.q.vex
                   ==
      ?~  q.wag
        wag
      [p.wag [~ [p.u.q.vex p.u.q.wag] q.u.q.wag]]

Examples

    > (scan "abc" (stir *@ add prn))
    294

    > (roll "abc" add)
    b=294

++stun

Parse several times

Parse bounded number of times.

Accepts

[[email protected] [email protected]] is a cell of atoms indicating the bounds.

fel is a ++rule.

Produces

A ++rule.

Source

    ++  stun                                                ::  parse several times
      |*  {lig/{@ @} fel/rule}
      |=  tub/nail
      ^-  (like (list _(wonk (fel))))
      ?:  =(0 +.lig)
        [p.tub [~ ~ tub]]
      =+  vex=(fel tub)
      ?~  q.vex
        ?:  =(0 -.lig)
          [p.vex [~ ~ tub]]
        vex
      =+  ^=  wag  %=  $
                     -.lig  ?:(=(0 -.lig) 0 (dec -.lig))
                     +.lig  ?:(=(0 +.lig) 0 (dec +.lig))
                     tub  q.u.q.vex
                   ==
      ?~  q.wag
        wag
      [p.wag [~ [p.u.q.vex p.u.q.wag] q.u.q.wag]]

Examples

    > ((stun [5 10] prn) [1 1] "aquickbrownfoxran")
    [p=[p=1 q=11] q=[~ [p="aquickbrow" q=[p=[p=1 q=11] q="nfoxran"]]]]

    > ((stun [5 10] prn) [1 1] "aquickbro")
    [p=[p=1 q=10] q=[~ [p="aquickbro" q=[p=[p=1 q=10] q=""]]]]

    > ((stun [5 10] prn) [1 1] "aqui")
    [p=[p=1 q=5] q=~]