Closed zim32 closed 3 years ago
Currently not really.
At the point that you want to map
, you have a ParseResult
. There you have the parsed data (the AST in your case) and the "remainder" of the input that you're parsing. Based on that remainder you can do some kind of positioning, but only if you compare it to the original input. You could make some kind of error like MySQL does:
SELECT * FROM `users` HERE `age` > 21;
would result in an error somewhat like
MySQL parse error at "HERE `age` > 21;"
which is basically the remainder.
However. Parsica knows how to make better error messages, so if you make a Parser that validates the Parsed map, you can make it fail like any other parser, and the error message will be something like
<input>:5:10
|
5 | ...bcd
| ^— column 10
Unexpected 'b'
Expecting 'a'
where the "unexpected" and "expected" input indications are based on the parser's label
Thanks for reply
Unfortunately if I try to print remainder I got error: Can't read the remainder of a failed ParseResult.
Correct, I forgot about that.
The return value of a parser is a ParseResult, which is either a Success or a Failure. In case of a Success we have a value and a remainder, in case of a Failure we have an error message.
something like this
ParseResult a = Succeed<a, Stream> | Fail<string>
Isn't the error message what you need?
F.e. I have AST node which handles function call. f.e. sum(1,2). It is represented by FunctionCallAstNode class. This class has validate() method where it checks whether function name is valid. And I need to display some error message that this function is invalid and point to position
so you're doing something like this?
$functionParser = ...;
map(
$functionParser,
function(array $parsedValues) {
$name = $parsedValues[0];
$parameters = $parsedValues[1];
return new FunctionCallAstNode($name, $parameters);
}
);
but you want to be able to let the parser fail if the FunctionCallAstNode
throws or if you call a validation method on it and it fails?
Let's first look at the types:
map :: Parser<T1> -> (T1 -> T2) -> Parser<T2>
This means that it takes a Parser
that will try to parse something of type T1
and a function from T1
to T2
, and it will return a Parser of type T2
.
However if i'm understanding you correctly, what you're trying to accomplish is that within the mapping function, you can still decide that you're not succeeding the parse, which is not possible with map
, according to the type signature.
The function that allows us to do something similar is the bind
function. Let's take a look at the type signature:
bind :: Parser<T1> -> (T1 -> Parser<T2>) -> Parser<T2>
This means that bind
takes a Parser
that will try to parse something of type T1
and a function from T1
to a Parser (!) that will try to parse a T2
, and it will return a Parser of type T2
.
Which as a result means that you can "replace" the outcome of your parser based on the function you passed.
Something like this:
$functionParser = ...;
bind(
$functionParser,
function(array $parsedValues) {
$name = $parsedValues[0];
$parameters = $parsedValues[1];
try {
$astNode = new FunctionCallAstNode($name, $parameters);
$astNode->validate(); // <-- this validate call should probably happen in the constructor of `FunctionCallAstNode`
} catch (Exception $e) {
return fail(); // <-- this will return a parser that will always fail
}
return pure($astNode)->label('Function Call AST Node'); // <-- this will return a parser that returns the AST Node
}
);
does that help you?
Yeas, I think this is what I need. Thanks. Yeas.my goal and question was how to validate something already parsed and fail it
Hi there. Thanks for great project!
I have a quesion. I need to validate AST which I build inside map(...) functions. But I don't know how to keep track of current position whie generating AST nodes to print meaningful messages. Is it possible to pass current position to map() function so it can be saved into AST node?