I wish to make my JavaCC parser return custom error messages that are more specific than the defaults.
I currently have a basic structure that is something like this:
Foo(): {} { (A() B())+ }
A(): {} { <TOKA1> | <TOKA2> }
B(): {} { <TOKB1> | <TOKB2> }
I have been researching how to throw custom error messages and the standard method seems to be something like:
A(): {} { <TOKA1> | <TOKA2>
| {throw new ParseException("Expected A, found " + getToken(1).image + ".");} }
However, implementing this on A and B causes the compiler to produce an error:
Expansion within "(...)+" can be matched by empty string.
This is understandable, since the parser does not 'know' that the empty options will terminate the parsing process. It predicts that it can match empty strings to infinity. Nevertheless, I can't find or think of any other easy way to throw errors like this. What is the best way of achieving my desired result?
Suppose your parser has taken one or more trips through the loop. Wouldn't you want the parser to leave the loop when the next token is not a TOKA1 or a TOKA2?
E.g. if the rest of your grammar is
void Start() : {} { Foo() C() <EOF> }
You definitely do not want an error if the input is
<TOKA1> <TOKB1> <TOKC> <EOF>
where <TOKC> is some token that could be at the start of a C.
So what I'd suggest is
Foo(): {} {
AForSure()
B()
(A() B())* }
AForSure() : {} { A() | {throw new ParseException("Expected A, found " + getToken(1).image + ".");} }
A(): {} { <TOKA1> | <TOKA2> }
B(): {} { <TOKB1> | <TOKB2> | {throw new ParseException("Expected B, found " + getToken(1).image + ".");} }}
That might not give you quite the quality of error messages you want. E.g. if the input is
<TOKA1> <TOKB1> <TOKB2> <EOF>
You might get an error "Expected C, found a TOKB2". So maybe you'll want to change that error to "Expected A or C, found a TOKB2".
Another way to approach it is to use recursion instead of looping. Suppose your list is always in parentheses, so, for example, the only use of Foo is in Bar and Bar looks like this
Bar() : {} { "(" Foo() ")" }
So you want to exit the Foo loop only when you hit a ")". Anything else is an error. You can rewrite the grammar as
Bar() : {} { "(" A("A") B() MoreFoos() }
MoreFoos() : {} { ")" | A("A or ')'") B() MoreFoos() }
A(String expected): {} { <TOKA1> | <TOKA2>
| {throw new ParseException("Expected "+expected+", found " + getToken(1).image + ".");} } }
B(): {} { <TOKB1> | <TOKB2>
| {throw new ParseException("Expected B, found " + getToken(1).image + ".");} }}
Related
I have working code which returns errors in the following way:
fn foo() -> anyhow::Result<()> {
...
Err(anyhow::Error::new(
MyError::MyVariant {
actual: 0,
expected: 1
}
))
}
Is there a more concise way of returning an instance of MyError?
(Omitting the anyhow::Error::new( wrapper causes a type-checking error, as MyError is not an instance of anyhow::Error.)
You can use the anyhow::bail! macro. It has exactly your usecase in mind:
use anyhow::bail;
fn foo() -> anyhow::Result<()> {
if errored {
bail!(MyError::MyVariant { actual: 0, expected: 1 })
}
}
As Filipe correctly points in the comments, if you find yourself checking simple conditions and returning an error you can simplify this step by using anyhow::ensure! which is quite similar to assert! but returns instead of panicing.
use anyhow::ensure;
fn foo() -> anyhow::Result<()> {
ensure!(!errored, MyError::MyVariant { actual: 0, expected: 1 });
}
#FunctionalInterface
interface Interf {
fun m1(num: Int)
}
fun main() {
val a: Interf = { 34 -> println("Hello world !!") }
}
Upon compilation getting this error
Unexpected tokens (use ';' to separate expressions on the same line)
Is Kotlin lambda function syntax is bit different from Java Lambda Expression?
First of all, this will not compile in java as well:
#FunctionalInterface
interface Interf {
void m1(int num);
}
class Main {
public static void main(String[] args) {
Interf f = 34 -> System.out.println("Hello world !!"); //compilation error here
}
}
With pretty the same error:
error: ';' expected
Interf f = 34 -> System.out.println("Hello world !!");
^
To make it correct, name of the lambda parameter should be changed, so that it become a valid java identifier.
For instance, this will compile:
Interf f = x -> System.out.println("Hello world !!");
Now, returning back to Kotlin. Version 1.4 introduces syntax for SAM conversions:
fun interface Interf {
fun m1(num: Int)
}
It could be instantiated with:
val a = Interf { println("Hello world !!") }
Convention of implicitly declared it parameter referring to lambda only parameter is preserved here.
How can I transform the following:
List<Try<String>>
to:
Try<List<String>>
Using kotlin and the functional library arrow (0.8.2). I would like to wrap it in a custom exception. It does not matter which one of the 'String' failed.
Update:
As the below answers will suffice, but I find it really hard to read. So, I implemented the following:
Create the following function:
fun getFailedStrings(result: List<Try<String>>): List<Failure> {
return result.fold(
initial = listOf(),
operation = { accumulator, nextUpdate ->
nextUpdate.fold(
ifSuccess = { accumulator },
ifFailure = { accumulator + Failure(it) }
)
})
}
Then use the result of the function:
return if (failedStrings.isNotEmpty()) {
failedStrings.first() // or whatever fits your usecase
} else {
// strings is the initial result of List<Try<String>>
Success(strings.mapNotNull { it.orNull() })
}
If we don't care about keeping the original exceptions we could do something like this with traverse:
val traversedTries = tries.traverse(Try.applicative(), ::identity)
This will return an instance of type Try<ListK<String>> with either all the strings or the first exception it finds.
ListK extends from List but we can optionally cast it by adding .map { it as List<String> } in the end if we need it to be Try<List<String>>
Alternatively, if we want to split the successes and failures we can create the following function:
fun <A> List<Try<A>>.splitSuccessFailure() : Tuple2<List<A>, List<Throwable>> =
fold(emptyList<A>() toT emptyList<Throwable>()) { (successes, failures), it ->
it.fold({ successes toT (failures + it) }, { (successes + it) toT failures })
}
Then, when we want to use it we can do the following:
val (successes, failures) = invalidTries.splitSuccessFailure()
Giving us two lists with the success values and failures respectively.
this seems to work:
fun convert(input: List<Try<String>>): Try<List<String>> =
input.fold(Try.just(emptyList())) { acc, i ->
acc.flatMap { list ->
i.flatMap {
Try.just(list + it)
}
}
}
I want to filter when specific exception occurs during execution of some of the upper chain function and try to retry the whole process only 3 times then if it still failes then give up. I came to something like this:
val disposable = someFunction(someParameter, delay, subject)
.flatMapCompletable { (parameter1, parameter2) ->
anotherFunction(parameter1, parameter2, subject)
}
.retryWhen { throwable ->
throwable.filter {
it.cause?.cause is ExampleException1
|| it.cause?.cause is ExampleException2
|| it.cause is ExampleException3
}
}
.andThen(someStuff())
.subscribe({
Timber.d("Finished!")
}, {
Timber.d("Failed!")
})
How to do it properly?
You may use zipWith with a range to achieve this.
.retryWhen { errors -> errors.zipWith(Observable.range(1, 3), { _, i -> i }) }
The retryWhen operator gives you the stream of all the errors from your source publisher. Here you zip these with numbers 1, 2, 3. Therefore the resulting stream will emit 3 next followed by the complete. Contrary to what you may think this resubscribes only twice, as the complete emitted immediately after the third next causes the whole stream to complete.
You may extend this further, by retrying only for some errors, while immediately failing for others. For example, if you want to retry only for IOException, you may extend the above solution to:
.retryWhen { errors -> errors
.zipWith(Observable.range(1, 3), { error, _ -> error })
.map { error -> when (error) {
is IOException -> error
else -> throw error
}}
}
Since map cannot throw a checked exception in Java, Java users may use flatMap for the same purpose.
I think what you're trying to do can be achieved with retry exclusively:
val observable = Observable.defer {
System.out.println("someMethod called")
val result1 = 2 // some value from someMethod()
Observable.just(result1)
}
observable.flatMap { result ->
// another method is called here but let's omit it for the sake of simplicity and throw some exception
System.out.println("someMethod2 called")
throw IllegalArgumentException("Exception someMethod2")
Observable.just("Something that won't be executed anyways")
}.retry { times, throwable ->
System.out.println("Attempt# " + times)
// if this condition is true then the retry will occur
times < 3 && throwable is IllegalArgumentException
}.subscribe(
{ result -> System.out.println(result) },
{ throwable -> System.out.println(throwable.localizedMessage) })
Output:
someMethod called
someMethod2 called
Attempt# 1
someMethod called
someMethod2 called
Attempt# 2
someMethod called
someMethod2 called
Attempt# 3
Exception someMethod2
As someMethod2 always throws an Exception, after 3 attempts Exception someMethod2 is printed in onError of the observer.
Code with exponential delay:
YourSingle()
.retryWhen { errors: Flowable<Throwable> ->
errors.zipWith(
Flowable.range(1, retryLimit + 1),
BiFunction<Throwable, Int, Int> { error: Throwable, retryCount: Int ->
if (error is RightTypeOfException && retryCount < retryLimit) {
retryCount
} else {
throw error
}
}
).flatMap { retryCount ->
//exponential 1, 2, 4
val delay = 2.toDouble().pow(retryCount.toDouble()).toLong() / 2
Flowable.timer(delay, TimeUnit.SECONDS)
}
}
I'm trying to find a way to execute requests in parallel and handle them when every observable finishes. Despite everything is working when all observables gives a response, I not seeing a way to handle each all errors when everything is finished.
This is a sample of zip operator, which basically executes 2 requests in parallel:
Observable.zip(
getObservable1()
.onErrorResumeNext { errorThrowable: Throwable ->
Observable.error(ErrorEntity(Type.ONE, errorThrowable))
}.subscribeOn(Schedulers.io()),
getObservable2()
.onErrorResumeNext { errorThrowable: Throwable ->
Observable.error(ErrorEntity(Type.TWO, errorThrowable))
}.subscribeOn(Schedulers.io()),
BiFunction { value1: String, value2: String ->
return#BiFunction value1 + value2
})
//execute requests should be on io() thread
.subscribeOn(Schedulers.io())
//there are other tasks inside subscriber that need io() thread
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ result ->
Snackbar.make(view, "Replace with your own action " + result, Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
},
{ error ->
Log.d("TAG", "Error is : " + (error as ErrorEntity).error.message)
}
)
private fun getObservable1(): Observable<String> {
return Observable.defer {
throw Throwable("Error 1")
}
}
private fun getObservable2(): Observable<String> {
return Observable.defer {
throw Throwable("Error 2")
}
}
Problem with this approach is that there is no mechanism to join each error like BiFunction do for the success case. Therefore, the zip operator will only trigger the first error and will ignore the others.
Output:
D/TAG: Error is : Error 1
Is there any way to retrieve all errors only after every observable inside zip completed or gave an error?
My main goal is to see which requests gave an error and execute only those after a dialog appears to the user asking him if he wants to retry the failed requests.
You can model your observables using data classes. E.g.
sealed class Response {
data class Success(val data: String) : Response()
data class Error(val t: Throwable) : Response()
}
then you can map your observables to Response like this:
val first: Observable<Response> = observable1
.map<Response> { Response.Success(it) }
.onErrorReturn { Response.Error(it) }
val second: Observable<Response> = observable2
.map<Response> { Response.Success(it) }
.onErrorReturn { Response.Error(it) }
and you can combine them:
Observable.zip(
first,
second,
BiFunction { t1: Response, t2: Response -> Pair(t1, t2) }
).subscribe({println(it)})
this prints:
(Error(t=java.lang.Exception: Error 1), Error(t=java.lang.Exception:
Error 2))
Also take a look at this article.