Hi folks, me again having great fun tinkering with crystal …
I sketched a parser framework in the spirit of rusty nom.
The resulting code looks like this:
children = optional(
enclosed(
enclosed(whitespace0, tag("{"), whitespace0),
delimited0(
whitespace1,
either(
boolean,
alphanumeric
)
),
enclosed(whitespace0, tag("}"), whitespace0),
)
).call(somebytes)
The central building block is a parser, which is a Proc
which takes Bytes
and eventually returns a Result
holding the parsed value and the remaining not consumed Bytes
: Proc(Bytes, Result(T)?)
. The parsed return value is generic so therefore the whole parser Proc is generic.
a sample of a working parser factory method:
# takes two parsers which are applied one after another
# both must succeed
# the result of the first one is dropped while the result
# of the second one is returned
def prefixed(prefix : Proc(Bytes, Result(A)?), parser : Proc(Bytes, Result(B)?)) forall A, B
Proc(Bytes, Result(B)?).new do |input|
if prefix_result = prefix.call(input)
parser.call(prefix_result.rest)
end
end
end
but now I 've got stuck on the following construct:
# takes a variable number of parsers
# returns the result of the first one that succeeds
def either(*variants : Proc(Bytes, Result(*T)?)) forall T
Proc(Bytes,Result(*T)?).new do |input|
match = nil
variants.each do |variant|
if match = variant.call(input)
break
end
end
match.try do |result|
Result(*T).new(result.value, result.rest)
end
end
end
This construct works when I call it with parsers of the same result type.
But if I try with parsers of different types (like in the sample above either(boolean, alphanumeric)
: the boolean parser is of type Bool whereas the alphanumeric parser is of type String), the compiler refuses to find a matching overload…
I took my inspiration from here Generics - Crystal … but I guess my current approach is too naive …
The basic idea is that I have multiple generic typed Procs as input and have to return a generic Proc where the generic type is the union of all generics from the input. So for example:
Proc(Bytes,Result(Bool)?) , Proc(Bytes,Result(String)?) => Proc(Bytes,Result(Bool|String)?)
Is this possible at all?!