mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-19 14:00:35 +00:00
add parser combinator test; uses multiple tuples/generics features
This commit is contained in:
105
tests/misc/parsecomb.nim
Normal file
105
tests/misc/parsecomb.nim
Normal file
@@ -0,0 +1,105 @@
|
||||
type Input[T] = object
|
||||
toks: seq[T]
|
||||
index: int
|
||||
|
||||
type
|
||||
ResultKind* = enum rkSuccess, rkFailure
|
||||
Result*[T, O] = object
|
||||
case kind*: ResultKind
|
||||
of rkSuccess:
|
||||
output*: O
|
||||
input: Input[T]
|
||||
of rkFailure:
|
||||
nil
|
||||
|
||||
type
|
||||
Parser*[T, O] = distinct proc (input: Input[T]): Result[T, O]
|
||||
|
||||
proc unit*[T, O](v: O): Parser[T, O] =
|
||||
Parser(proc (inp: Input[T]): Result[T, O] =
|
||||
Result[T, O](kind: rkSuccess, output: v, input: inp))
|
||||
|
||||
proc fail*[T, O](): Parser[T, O] =
|
||||
Parser(proc (inp: Input[T]): Result[T, O] =
|
||||
Result(kind: rkFailure))
|
||||
|
||||
method runInput[T, O](self: Parser[T, O], inp: Input[T]): Result[T, O] =
|
||||
# hmmm ..
|
||||
type tmp = proc (input: Input[T]): Result[T, O]
|
||||
# XXX: above needed for now, as without the `tmp` bit below, it compiles to invalid C.
|
||||
tmp(self)(inp)
|
||||
|
||||
method run*[T, O](self: Parser[T, O], toks: seq[T]): Result[T, O] =
|
||||
self.runInput(Input[T](toks: toks, index: 0))
|
||||
|
||||
method chain*[T, O1, O2](self: Parser[T, O1], nextp: proc (v: O1): Parser[T, O2]): Parser[T, O2] =
|
||||
Parser(proc (inp: Input[T]): Result[T, O2] =
|
||||
let r = self.runInput(inp)
|
||||
case r.kind:
|
||||
of rkSuccess:
|
||||
nextp(r.output).runInput(r.input)
|
||||
of rkFailure:
|
||||
Result[T, O2](kind: rkFailure))
|
||||
|
||||
method skip[T](self: Input[T], n: int): Input[T] =
|
||||
Input[T](toks: self.toks, index: self.index + n)
|
||||
|
||||
proc pskip*[T](n: int): Parser[T, tuple[]] =
|
||||
Parser(proc (inp: Input[T]): Result[T, tuple[]] =
|
||||
if inp.index + n <= inp.toks.len:
|
||||
Result[T, tuple[]](kind: rkSuccess, output: (), input: inp.skip(n))
|
||||
else:
|
||||
Result[T, tuple[]](kind: rkFailure))
|
||||
|
||||
proc tok*[T](t: T): Parser[T, T] =
|
||||
Parser(proc (inp: Input[T]): Result[T, T] =
|
||||
if inp.index < inp.toks.len and inp.toks[inp.index] == t:
|
||||
pskip[T](1).then(unit[T, T](t)).runInput(inp)
|
||||
else:
|
||||
Result[T, T](kind: rkFailure))
|
||||
|
||||
proc `+`*[T, O](first: Parser[T, O], second: Parser[T, O]): Parser[T, O] =
|
||||
Parser(proc (inp: Input[T]): Result[T, O] =
|
||||
let r = first.runInput(inp)
|
||||
case r.kind
|
||||
of rkSuccess:
|
||||
r
|
||||
else:
|
||||
second.runInput(inp))
|
||||
|
||||
# end of primitives (definitions involving Parser(..))
|
||||
|
||||
method map*[T, O1, O2](self: Parser[T, O1], p: proc (v: O1): O2): Parser[T, O2] =
|
||||
self.chain(proc (v: O1): Parser[T, O2] =
|
||||
unit[T, O2](p(v)))
|
||||
|
||||
method then*[T, O1, O2](self: Parser[T, O1], next: Parser[T, O2]): Parser[T, O2] =
|
||||
self.chain(proc (v: O1): Parser[T, O2] =
|
||||
next)
|
||||
|
||||
proc `*`*[T, O1, O2](first: Parser[T, O1], second: Parser[T, O2]): Parser[T, (O1, O2)] =
|
||||
first.chain(proc (v1: O1): Parser[T, (O1, O2)] =
|
||||
second.map(proc (v2: O2): (O1, O2) =
|
||||
(v1, v2)))
|
||||
|
||||
proc repeat0*[T, O](inner: Parser[T, O]): Parser[T, seq[O]] =
|
||||
var nothing = unit[T, seq[O]](@[])
|
||||
inner.chain(proc(v: O): Parser[T, seq[O]] =
|
||||
repeat0(inner).map(proc(vs: seq[O]): seq[O] =
|
||||
@[v] & vs)) + nothing
|
||||
|
||||
proc repeat1*[T, O](inner: Parser[T, O]): Parser[T, seq[O]] =
|
||||
inner.chain(proc(v: O): Parser[T, seq[O]] =
|
||||
repeat0(inner).map(proc(vs: seq[O]): seq[O] =
|
||||
@[v] & vs))
|
||||
|
||||
proc leftRec*[T, O, A](inner: Parser[T, O], after: Parser[T, A], fold: proc(i: O, a: A): O): Parser[T, O] =
|
||||
(inner*repeat0(after)).map(proc(ias: (O, seq[A])): O =
|
||||
var (i, asx) = ias
|
||||
for a in asx:
|
||||
i = fold(i, a)
|
||||
i)
|
||||
|
||||
proc lazy*[T, O](inner: proc(): Parser[T, O]): Parser[T, O] =
|
||||
unit[T, tuple[]](()).chain(proc(v: tuple[]): Parser[T, O] =
|
||||
inner())
|
||||
55
tests/misc/tparsecombnum.nim
Normal file
55
tests/misc/tparsecombnum.nim
Normal file
@@ -0,0 +1,55 @@
|
||||
import parsecomb
|
||||
|
||||
discard """
|
||||
output: "-289096"
|
||||
"""
|
||||
|
||||
type Num = int
|
||||
|
||||
# forward stuff
|
||||
var exp3: Parser[string, Num]
|
||||
var exp = lazy(proc(): Parser[string, Num] = exp3)
|
||||
|
||||
var digit = (proc(): Parser[string, Num] =
|
||||
result = tok("0").then(unit[string, Num](Num(0)))
|
||||
for n in 1..9:
|
||||
result = result + tok($n).then(unit[string, Num](Num(n)))
|
||||
)()
|
||||
|
||||
var num = repeat1(digit).map(proc(ds: seq[Num]): Num =
|
||||
result = 0
|
||||
for d in ds:
|
||||
result = result*10 + d)
|
||||
|
||||
type Op = proc(a, b: Num): Num
|
||||
|
||||
var plusOp = tok("+").then(unit[string, Op](proc(a, b: Num): Num = a + b))
|
||||
var minusOp = tok("-").then(unit[string, Op](proc(a, b: Num): Num = a - b))
|
||||
var timesOp = tok("*").then(unit[string, Op](proc(a, b: Num): Num = a*b))
|
||||
var divideOp = tok("/").then(unit[string, Op](proc(a, b: Num): Num = a div b))
|
||||
|
||||
var paren = (tok("(") * exp * tok(")")).map(proc(ler: ((string, Num), string)): Num =
|
||||
var (le, r) = ler
|
||||
var (l, e) = le
|
||||
e)
|
||||
|
||||
proc foldOp(a: Num, ob: (Op, Num)): Num =
|
||||
var (o, b) = ob
|
||||
o(a, b)
|
||||
|
||||
var exp0 = paren + num
|
||||
var exp1 = exp0.leftRec((timesOp + divideOp)*exp0, foldOp)
|
||||
var exp2 = exp1.leftRec((plusOp + minusOp)*exp1, foldOp)
|
||||
exp3 = exp2
|
||||
|
||||
proc strsplit(s: string): seq[string] =
|
||||
result = @[]
|
||||
for i in 0 .. s.len - 1:
|
||||
result.add($s[i])
|
||||
|
||||
var r = exp.run("523-(1243+411/744*1642/1323)*233".strsplit)
|
||||
case r.kind:
|
||||
of rkSuccess:
|
||||
echo r.output
|
||||
of rkFailure:
|
||||
echo "failed"
|
||||
Reference in New Issue
Block a user