Hi guys!
I'm doing an experiment in creating some small parser combinator to eventually parse JSON. I thought I could create a type that is the function signature of the function to return from jsObj - so it would be a bit better to read. But there seems to be something wrong with my code, because I get the error message "cannot instantiate return:type" when trying to compile. My code is like this:
from std/parseopt import initOptParser, next
from std/sugar import collect, `->`, `=>`
from std/strutils import find
type
ParseResult = ref object
index: int
result: seq[string]
Parser = (string) -> ParseResult | CatchableError
proc jsObj(input: string): Parser =
return
proc (c: string): ParseResult | CatchableError =
let pos = input.find(c)
if pos == -1:
return CatchableError()
else:
return ParseResult(index: pos, result: @[c])
proc parseJSON*(content: string) =
let objParser = jsObj("{}")
I've tried several things, including using the the expected function signature explicitly, like this:
proc jsObj(input: string): (proc (x: string): ParseResult | CatchableError) =
But it's giving me the same error. Can someone give me a hint about what the problem is or if this is even the right way to do it? I think I could maybe achieve the same result using templates, couldn't I?
Thanks in advance for all your help!
Ok, my next probably stupid step is: I want to create a parser combinator function that takes a variable amount of other parser combinators and creates a new one. I thought about using a varargs[Parser] parameter, but I think I unfortunately don't understand it correctly at that point. My code is:
type
ParseResult* = ref object
case success*: bool
of true:
value*: string
rest*: string
of false:
error*: string
Parser* = (string) -> ParseResult
proc character*(c: char): Parser =
return
proc (input: string): ParseResult =
if input[0] == c:
return ParseResult(success: true, value: $c, rest: input.substr(1))
else:
return ParseResult(success: false, error: &"Couldn't find {c}")
proc either*(parsers: varargs[Parser]): Parser =
return
proc (input: string): ParseResult =
for p in parsers:
let res = p(input)
if res.success:
return res
return ParseResult(success: false, error: "Couldn't match either parsers")
But this gives me the error message: "Error: 'parsers' is of type <varargs[Parser]> which cannot be captured as it would violate memory safety"
How does varargs work with a procedural type like Parser?
https://github.com/loloicci/nimly
https://github.com/choltreppe/parlexgen
https://github.com/haxscramper/hparse
https://github.com/erhlee-bird/bnimf
https://github.com/beef331/commander
There are excellent parser generator available. Please study how they work. "Combintators" are a kludge you come up with when your PL lacks decent DSL capabilities.
I think it's:
proc either*(parsers: varargs[Parser]): Parser =
let parsers = @parsers
return
proc (input: string): ParseResult =
for p in parsers:
let res = p(input)
if res.success:
return res
return ParseResult(success: false, error: "Couldn't match either parsers")
For your case.
I think you could store the varargs in a local seq and then capture that
import std/sequtils
proc either*(parsers: varargs[Parser]): Parser =
let parsers = parsers.toSeq
return
proc (input: string): ParseResult =
for p in parsers:
let res = p(input)
if res.success:
return res
return ParseResult(success: false, error: "Couldn't match either parsers")